Каковы основные виды использования yield() и чем оно отличается от join() и interrupt()?

Я немного запутался в использовании метода yield() в Java, в частности, в приведенном ниже примере кода. Я также прочитал, что yield() используется для предотвращения выполнения потока.

Мои вопросы:

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

  • Каковы, по сути, основные виды использования yield()?

  • Каким образом yield() отличается от методов join() и interrupt()?

Пример кода:

public class MyRunnable implements Runnable {

   public static void main(String[] args) {
      Thread t = new Thread(new MyRunnable());
      t.start();

      for(int i=0; i<5; i++) {
          System.out.println("Inside main");
      }
   }

   public void run() {
      for(int i=0; i<5; i++) {
          System.out.println("Inside run");
          Thread.yield();
      }
   }
}

Я получаю тот же вывод, используя код выше и с использованием и без использования yield():

Inside main
Inside main
Inside main
Inside main
Inside main
Inside run
Inside run
Inside run
Inside run
Inside run

Ответ 1

Источник: http://www.javamex.com/tutorials/threads/yield.shtml

Окна

В реализации Hotspot способ, которым работает Thread.yield(), изменено между Java 5 и Java 6.

В Java 5, Thread.yield() вызывает вызов Windows API Sleep(0). Эта имеет особый эффект очистки текущего кванта потока и помещая его в конец очереди для уровня приоритета. В других слова, все исполняемые потоки с одним и тем же приоритетом (и те из них, которые больше приоритет) получит шанс запустить до того, как следующий поток будет следующий заданное время процессора. Когда он будет перепланирован, он вернется с полным полным квантом, но не "переносит" любой из оставшийся квант с момента урожайности. Это поведение немного отличается от ненулевого сна, где спящая нить обычно теряет 1 квантовое значение (по сути, 1/3 от 10 или 15 мс тика).

В Java 6 это поведение было изменено. В настоящее время виртуальная машина Hotspot Thread.yield() с помощью вызова API SwitchToThread() Windows SwitchToThread(). Этот вызов делает текущий поток отказом от текущего времени, но не его весь квант. Это означает, что в зависимости от приоритетов других потоки, текущая нить может быть запланирована на одно прерывание спустя период. (Смотрите раздел планирование потоков для получения дополнительной информации. информация о тайм-листах.)

Linux

В Linux, Hotspot просто вызывает sched_yield(). Последствия этот призыв несколько отличается и, возможно, более серьезным, чем в Окна:

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

(см. раздел планирование потоков для получения более подробной информации о приоритетах и алгоритмы планирования.)

Когда использовать yield()?

Я бы сказал практически никогда. Его поведение не является стандартным и, как правило, лучшие способы выполнения задач, которые вы выполняете может потребоваться выполнить с yield():

  • Если вы пытаетесь использовать только часть процессора, вы можете сделать это более управляемым способом, оценив, сколько CPU - поток использовал в своем последнем фрагменте обработки, затем спать для некоторых количество времени для компенсации: см. метод sleep();
  • если вы ожидаете процесс или ресурс, чтобы завершить или стать доступным, есть более эффективные способы для этого, например, используя join(), чтобы дождаться завершения другого потока, используя механизм wait/notify, чтобы один поток мог сигнализировать другому что задача завершена или, в идеале, с использованием одной из Java 5 concurrency создаются такие, как Semaphore или блокировка очереди.

Ответ 2

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

Как известно, yield заставляет вызывающий поток отказаться от процессора, с которым он работает, чтобы можно было планировать другой поток. Это полезно, когда текущий поток уже завершил свою работу, но хочет быстро вернуться к передней части очереди и проверить, не изменилось ли какое-либо условие. Как это отличается от переменной условия? yield позволяет потоку быстрее возвращаться в рабочее состояние. При ожидании переменной состояния поток приостанавливается и должен ждать другого потока, чтобы сигнализировать о его продолжении. yield в основном говорит: "Позвольте запустить другой поток, но позвольте мне вернуться к работе очень скоро, поскольку я ожидаю, что что-то изменится в моем состоянии очень быстро". Это указывает на занятость прядения, где условие может быстро измениться, но приостановление потока приведет к большому результату.

Но достаточно лепет, вот конкретный пример: параллельный узор с волновым фронтом. Основным примером этой проблемы является вычисление отдельных "островов" 1s в двумерном массиве, заполненном 0s и 1s. "Остров" представляет собой группу ячеек, которые расположены рядом друг с другом по вертикали или по горизонтали:

1 0 0 0
1 1 0 0
0 0 0 1
0 0 1 1
0 0 1 1

Здесь у нас есть два острова 1s: сверху-слева и внизу справа.

Простое решение состоит в том, чтобы сделать первый проход по всему массиву и заменить значения 1 с помощью счетчика приращения, так что к концу каждый 1 был заменен его порядковым номером в следующем порядке:

1 0 0 0
2 3 0 0
0 0 0 4
0 0 5 6
0 0 7 8

В следующем шаге каждое значение заменяется минимальным значением между собой и его соседними значениями:

1 0 0 0
1 1 0 0
0 0 0 4
0 0 4 4
0 0 4 4

Теперь мы можем легко определить, что у нас есть два острова.

Часть, которую мы хотим запустить параллельно, - это шаг, на котором мы вычисляем минимумы. Не вдаваясь в подробности, каждый поток получает строки чередующимся образом и опирается на значения, вычисленные потоком, обрабатывающим строку выше. Таким образом, каждый поток должен немного отставать от потока, обрабатывающего предыдущую строку, но также должен идти в ногу в разумные сроки. Более подробная информация и реализация представлены мной в этом документе. Обратите внимание на использование sleep(0), которое более или менее эквивалентно C yield.

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

Как вы можете видеть, yield - довольно тонкая оптимизация. Использование его в неправильном месте, например. ожидание при условии, что изменения редко происходят, вызовет чрезмерное использование CPU.

Извините за длинный лепет, надеюсь, что я ясно дал понять.

Ответ 3

О различиях между yield(), interrupt() и join() - в общем, не только в Java:

  • урожай. Буквально, чтобы "уступить" означает отпустить, сдаться, сдаться. Придающий поток сообщает операционной системе (или виртуальной машине, или нет), которая готова разрешить другим потокам. Это указывает на то, что он не делает что-то слишком критичное. Это лишь намек, но не гарантированный эффект.
  • присоединение. Когда несколько потоков присоединяются к определенному дескриптору или токену или сущности, все они ждут завершения всех других соответствующих потоков (полностью или до их собственного соответствующего объединения). Это означает, что куча потоков выполнила все свои задачи. Затем каждый из этих потоков может планировать продолжение другой работы, имея возможность предположить, что все эти задачи действительно завершены. (Не путать с SQL-соединениями!)
  • прерывание. Используется одним потоком, чтобы "вытолкнуть" другой поток, который спит, или ждет, или присоединяется - так, что он будет продолжать работать снова, возможно, с указанием, что оно было прервано, (Не путать с аппаратными прерываниями!)

В частности, для Java см.

Ответ 4

Во-первых, фактическое описание

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

Теперь очень вероятно, что ваш основной поток выполнит цикл пять раз до того, как будет выполнен метод run нового потока, поэтому все вызовы yield будут выполняться только после того, как цикл в главном выполняется поток.

join остановит текущий поток до тех пор, пока исполняемый поток с join() не будет выполнен.

interrupt будет прерывать поток, на который он вызывается, вызывая InterruptedException.

yield позволяет переключать контекст на другие потоки, поэтому этот поток не будет потреблять весь процесс использования процессора.

Ответ 5

Каковы, по сути, основные виды использования yield()?

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

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

НЕТ, они будут давать разные результаты. Без yield(), как только поток получает контроль, он будет выполнять цикл "Inside run" за один раз. Однако с выходом(), как только поток получит контроль, он будет печатать "Inside run" один раз, а затем передаст управление другому потоку, если он есть. Если нить в ожидании, этот поток снова будет возобновлен. Поэтому каждый раз, когда выполняется "Inside run" , он будет искать другие потоки для выполнения, и если нить не доступна, текущий поток будет продолжать выполнять.

Каким образом метод yield() отличается от методов join() и interrupt()?

yield() предназначен для размещения других важных потоков, join() предназначен для ожидания выполнения другого потока, и прерывание() предназначено для прерывания текущего исполняемого потока, чтобы сделать что-то еще.

Ответ 6

Thread.yield() приводит к тому, что поток переходит из состояния "Запуск" в состояние "Runnable". Примечание. Это не приводит к тому, что поток переходит в состояние "Ожидание".

Ответ 7

1.With и без использования метода доходности в приведенном ниже коде же выход получается. Правильно ли это?

Правильный вывод в этом случае не указан, поэтому все правильно.

2. Как правило, основное использование yield()

Он почти никогда не использовался вообще, не говоря уже о достаточно, чтобы определить "основное использование". Я никогда не использовал метод yield() на любой платформе, и я использовал их для меня более 20 лет, и за этот период я ​​занимался довольно серьезным системным программированием.

3.Как он отличается от методов join() и interrupt()

Это отличается тем, что это не одно и то же. Вопрос совершенно бессмыслен.

Ответ 8

Thread.yield()

Когда мы вызываем метод Thread.yield(), планировщик потоков сохраняет текущий выполняемый поток в состояние Runnable и выбирает другой поток с равным приоритетом или более высоким приоритетом. Если нет ни одного равного и более высокого приоритетного потока, он перенаправляет поток вызова(). Помните, что метод yield не приводит поток в состояние Wait или Blocked. Он может только сделать поток из Running State в Runnable State.

Join()

Когда join вызывается экземпляром потока, этот поток будет сообщать текущему исполнению потока, чтобы он дождался завершения потока объединения. Соединение используется в ситуациях, когда задача, которая должна быть выполнена до завершения текущей задачи, будет завершена.

Ответ 9

yield() основное использование используется для удержания многопоточного приложения.

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