Ну, название в основном говорит само за себя, с небольшим дополнением, что я действительно хотел бы знать, когда их использовать. И это может быть достаточно просто - я читал документацию к ним обоим, но до сих пор не вижу большой разницы.
Здесь есть такие ответы, как это, которые в основном говорят:
Yielding also was useful for busy waiting...
Я не могу с ними согласиться по той простой причине, что ForkJoinPool использует Thread::yield внутри, а это довольно недавнее дополнение в мире jdk.
Что меня действительно беспокоит, так это использование в jdk (StampledLock::tryDecReaderOverflow):
else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
Thread.yield();
else
Thread.onSpinWait();
return 0L;
Так что, кажется, есть случаи, когда одно было бы предпочтительнее другого. И нет, у меня нет фактического примера, где мне это может понадобиться - единственный, который я действительно использовал, был Thread::onSpinWait, потому что 1) я был занят ожиданием 2) имя в значительной степени говорит само за себя, что я должен использовал его в занятый спин.
Возможный дубликат Метод onSpinWait() класса Thread
@JohnKugelman Я никогда не нужный - я просто знал, что этот метод существует, и в его документации есть пример, который очень близко соответствует тому, что у меня был, поэтому решил использовать его, в надежде, что он каким-то образом улучшит производительность. Я также никогда не нуждался в активном вращении, во-первых, это было похоже на то, что я должен был обязательно попробовать.
@Torben, учитывая, что я дал один из ответов на этот вопрос, я не думаю, что это дубликат.
В примере с API показан цикл, вращающийся с флагом volatile. Это нормально, если вы реализуете низкоуровневый примитив блокировки. Скорее всего, большинство людей, читающих это, не являются. Им следует держаться подальше от такой идиомы. Вращение по переменной предназначено только для полных экспертов и полных новичков.
@JohnKugelman хм .. Я был бы во второй группе, просто мне показалось, что я должен был попробовать, и это не так уж сложно, если все, что делает занятое вращение, действует как монитор.




При блокировке темы есть несколько стратегий на выбор: вращение, wait()/notify() или их комбинация. Чистое вращение на переменной — это стратегия с очень малой задержкой, но она может привести к голоданию других потоков, которые борются за процессорное время. С другой стороны, wait() / notify() высвобождает ЦП для других потоков, но может стоить тысячи циклов ЦП из-за задержки при удалении/планировании потоков.
Итак, как мы можем избежать чистого вращения, а также накладных расходов, связанных с депланированием и планированием заблокированного потока?
Thread.yield() — это подсказка планировщику потоков отказаться от своего кванта времени, если готов другой поток с таким же или более высоким приоритетом. Это позволяет избежать чистого вращения, но не позволяет избежать накладных расходов на повторное планирование потока.
Последним дополнением является Thread.onSpinWait(), который вставляет специфичные для архитектуры инструкции, чтобы намекнуть процессору, что поток находится в цикле вращения. На x86 это, наверное, инструкция PAUSE, на aarch64 это инструкция YIELD.
Какая польза от этих инструкций? В чистом цикле вращения процессор будет спекулятивно выполнять цикл снова и снова, заполняя конвейер. Когда переменная, на которой вращается поток, окончательно изменится, вся эта спекулятивная работа будет отброшена из-за нарушения порядка памяти. Какая трата!
Подсказка процессору может помешать конвейеру спекулятивно выполнить спин-цикл до тех пор, пока предыдущие инструкции памяти не будут зафиксированы. В контексте SMT (гиперпоточности) это полезно, так как конвейер освобождается для других аппаратных потоков.
Таким образом, onSpinWait() — это правильно, если мы ожидаем, что другой поток уже работает на другом процессоре (ядре), выполняющем условие, тогда как yield() — это правильно, если мы ожидаем, что у другого потока нет процессорного времени. К сожалению, мы не можем этого знать, поэтому пример кода, показанный в вопросе, использует некоторую случайную эвристику, чтобы решить, когда какой метод вызывать.
Можете ли вы описать сценарий, в котором вам нужно было бы ждать вращения? Мне, как пользователю Java, а не разработчику, это никогда не требовалось. Конечно, в программировании ядра спин-блокировки используются повсеместно. А на Яве? Я не могу придумать вариант использования, в котором я бы крутил ожидание, а не использовал блокировку, монитор или асинхронный обратный вызов. (Я не шучу. Спрашивать, какой из них использовать, возможно, неправильный вопрос, если ответ таков: вряд ли кому-то следует использовать любой из них.)