Как работает метод yield? - PullRequest
8 голосов
/ 01 марта 2011

В javadoc сказано, что метод доходности

Заставляет временно исполняемый объект потока временно приостанавливать и разрешать выполнение другим потокам.

А в книге Кэтрин Сьерра и Берт Бейтс говорится, что

yield () должен сделать это верните работающий поток обратно в runnable, чтобы позволить другим потокам тот же приоритет, чтобы получить свою очередь.

Так что же на самом деле делает метод?

Ответы [ 7 ]

12 голосов
/ 01 марта 2011

Для многопоточного приложения yield приведет к тому, что выполняющийся в данный момент поток приостановит выполнение и будет переведен в состояние ожидания. Затем JVM начнет запускать другой поток, который ранее находился в состоянии ожидания.

Я полагаю, что тот же поток, который только что получен, технически может быть запланирован для повторного запуска.

А мне еще предстоит увидеть это в дикой природе. Поэтому я думаю, что это безопасно избегать.

Для уточнения:

В многопоточной среде потоки по расписанию и внеплановому расписанию отключаются и включаются по желанию JVM. Таким образом, даже если yield не вызывается в коде, ваш поток может / будет автоматически уступать другим потокам, когда JVM решит, что должно. Это позволяет многопоточности работать в среде только с одним ядром обработки.

Вызов yield возвращает просто JVM для перевода текущего потока в состояние ожидания, даже если JVM не собиралась.

Я попробую иллюстрацию:
Ниже приведена очень упрощенная иллюстрация выполнения двух потоков с течением времени (предположим, одно ядро) -

Thread\Time    1    2    3    4    5    6    7    8    9
Thread 1    -----------       -----          -------
Thread 2               -------     ----------       ------

Всякий раз, когда вы видите '-', это означает, что выполняется поток. ' ' означает, что поток ожидает. Как видите, одновременно может работать только 1 поток. Итак, пока работает 1, другой ждет. То, что предполагается сделать с помощью yield, - это дать другим потокам шанс опередить текущий запущенный поток.

2 голосов
/ 01 марта 2011

В конечном счете, вызов yield() приводит к вызову таких методов os, которые в принципе помещают саму задачу обратно в очередь выполнения и позволяют запустить следующую задачу ( source ):

 /**
  * sys_sched_yield - yield the current processor to other threads.
  *
  * This function yields the current CPU to other tasks. If there are no
  * other threads running on this CPU then this function will return.
  */
 SYSCALL_DEFINE0(sched_yield)
 {
        /*
         *  lock this runqueue and disable interrupts.
         */
         struct rq *rq = this_rq_lock();

         schedstat_inc(rq, yld_count);
         current->sched_class->yield_task(rq);

         /*
          * Since we are going to call schedule() anyway, there's
          * no need to preempt or enable interrupts:
          */
         __release(rq->lock);
         spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
         _raw_spin_unlock(&rq->lock);
         preempt_enable_no_resched();

         schedule();

         return 0;
 }
2 голосов
/ 01 марта 2011

метод yield () предназначен для того, чтобы гарантировать, что все потоки с одинаковым приоритетом в приложении не вызовут голодание .Например, в приложении есть пять потоков, и все они имеют одинаковый приоритет.Теперь предположим, что один поток получил шанс на запуск, и этот поток занимает так много времени, чтобы выполнить свою задачу, и, следовательно, другие потоки не получат шанс на запуск.Поэтому, чтобы избежать подобных ситуаций, yield () должен спасти.

2 голосов
/ 01 марта 2011

Происходит со времени совместной многозадачности. Основная идея заключается в том, что процессор выполняет только один поток до:

  1. эта тема заканчивается
  2. этот поток выполняет некоторую блокирующую операцию, например object.wait() или Thread.sleep, ожидая завершения какой-либо операции ввода-вывода, ожидая некоторый объектный монитор или аналогичный.
  3. эта тема вызывает Thread.yield().

В каждом из этих случаев планировщик потока затем выбирает другой поток для выполнения. Таким образом, чтобы быть справедливым для других потоков, вы бы в более длинных циклах без каких-либо операций блокировки регулярно вызывали yield(). (Если ни один другой поток не готов к запуску, тогда тот же поток будет запланирован снова, поэтому не будет большой потери производительности.)

В современных виртуальных машинах переключение потоков может происходить в любой точке, не только в перечисленных, потоки могут даже выполняться одновременно, поэтому в этом нет особой необходимости, и некоторые виртуальные машины могут вообще ее игнорировать (аналогично System.gc().)

2 голосов
/ 01 марта 2011

yield() обычно используется, когда вы ожидаете в потоке чего-либо, но не хотите блокировать циклы CPC с помощью чего-то вроде while(condition){ ...}. Метод yield () отличается от платформы к платформе и зависит от планировщика потоков, и вы не должны полагаться на его поведение определенным образом.

2 голосов
/ 01 марта 2011

Потоки могут находиться в состоянии готовности (выполнения), блокировки (например, ожидание завершения ввода-вывода) или выполнения; это является общим для всех реализаций потока, хотя некоторые конкретные реализации могут иметь больше состояний.

Выход приводит к тому, что поток переключается с работающего на работоспособный, и ждет, пока планировщик не переключит его на повторный запуск в будущем. Вот что подразумевается в книге SCJP.

В ветке кажется, что она была приостановлена ​​на некоторое время, как описано в javadoc. Так что оба утверждения верны, просто по-разному сформулированы.

0 голосов
/ 14 июля 2019

Надеюсь, это поможет!

package yield;

public class ThreadYieldApp {

    Thread th1 = new Thread("Thread 1") {
        public void run() {

            for(int i = 0; i <= 10; i++) {
                System.out.println("Before Yield - " + Thread.currentThread().getName() + " at index - " + i);
                //Currently pauses the thread and checks for other threads of same priority, and passes control to it based on Thread Scheduler pick 
                Thread.yield();
                System.out.println("Currently running - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };

    Thread th2 = new Thread("Thread 2") {
        public void run() {
            for(int i = 0; i <= 10; i++) {
                System.out.println("Currently running - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };


    public static void main(String[] args) {
        ThreadYieldApp threadYieldApp = new ThreadYieldApp();
        threadYieldApp.th1.start();
        threadYieldApp.th2.start();
    }

    //Happy coding -- Parthasarathy S
}
...