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