Метод onSpinWait() класса Thread
В то время как обучение Java 9 я наткнулся на новый метод Thread
класса, называемого onSpinWait
. Согласно Javadocs, этот метод используется для этого:
Указывает, что вызывающий в данный момент не может прогрессировать, пока не произойдет одно или несколько действий со стороны других действий.
Может ли кто-нибудь помочь мне понять этот метод, приводя реальный пример?
Ответы
Ответ 1
Он такой же (и, вероятно, компилируется) как код операции PAUSE
x86 и эквивалентен макросу Win32 YieldProcessor
, GCC __mm_pause()
и методу С# Thread.SpinWait
Это очень слабая форма уступки: она говорит вашему ЦП, что вы находитесь в цикле, который может сжечь много циклов ЦП, ожидая, что что-то произойдет (ожидание занятости).
Таким образом, CPU может назначать больше ресурсов другим потокам, фактически не загружая планировщик ОС и не снимая с себя готовый к запуску поток (который может быть дорогим).
распространенное использование для этого - спин-блокировка, когда вы знаете, что конфликт в разделяемой памяти очень редок или заканчивается очень быстро, спин-блокировка может быть лучше, чем обычная блокировка.
Псевдокод для таких может выглядеть так:
int state = 0; //1 - locked, 0 - unlocked
routine lock:
while state.cas(new_value=1, wanted_value=0) == false
yield
routine unlock:
atomic_store(state,0)
yield
может быть реализован с помощью Thread.onSpinWait()
, намекая на то, что при попытке блокировки блокировки ЦП может выделять больше ресурсов другим потокам.
Этот метод выдачи чрезвычайно распространен и популярен при реализации алгоритма без блокировки, так как большинство из них зависят от ожидания занятости (которое реализуется почти всегда как атомарный цикл сравнения и замены). это имеет любое реальное использование, которое вы можете себе представить.
Ответ 2
Чистая система подсказки!
Чтение в этой статье Я цитирую:
Цели
Определите API, который позволит Java-коду намекать на систему времени выполнения, что она находится в цикле вращения. API будет чистым намеком и не будет иметь никаких требований к семантическому поведению (например, no-op является допустимой реализацией). Позвольте JVM извлечь выгоду из поведения, связанного с циклом спина, которое может быть полезно на некоторых аппаратных платформах. Обеспечьте как непростую реализацию, так и внутреннюю реализацию в JDK, и продемонстрируйте преимущества выполнения как минимум для одной крупной аппаратной платформы.
Существует много раз, когда поток должен быть приостановлен до тех пор, пока что-то вне его области не изменится. Обычной практикой A (один раз) был шаблон wait() notify()
, где один поток ожидал, когда другой поток разбудит их.
Для этого существует большое ограничение, а именно, другой поток должен знать, что могут быть ожидающие потоки и должны уведомлять. Если работа другого потока находится вне вашего контроля, нет способа получить уведомление.
Единственный способ - ожидание спина. скажем, у вас есть программа, которая проверяет наличие новых писем и уведомляет пользователя:
while(true) {
while(!newEmailArrived()) {
}
makeNotification();
}
Этот кусок кода будет выполняться миллионы раз в секунду; вращаясь снова и снова, используя драгоценное электричество и мощность процессора. Обычный способ сделать это - подождать несколько секунд на каждой итерации.
while(true) {
while(!newEmailArrived()) {
try {
Thread.sleep(5000);
} catch(InterruptedException e) {
}
}
makeNotification();
}
Это очень хорошая работа. Но в тех случаях, когда вы должны немедленно работать, сон может быть и речи.
Java 9 пытается решить эту проблему, введя этот новый метод:
while(true) {
while(!newEmailArrived()) {
Thread.onSpinWait();
}
makeNotification();
}
Это будет работать точно так же, как без вызова метода, но система может снизить приоритет процесса; замедляя цикл или уменьшая электричество в этом цикле, когда его ресурсы необходимы для других более важных вещей.
Ответ 3
Я просто хочу добавить свои 2 цента после прочтения документа и исходного кода для него. Этот метод может вызвать некоторые оптимизации и, возможно, не так - поэтому нужно быть осторожным - вы не можете положиться на него - так как это hint
для CPU
больше, чем требуется, вероятно, нет никаких основополагающих ссылок в любом случае. Это означает, что фактический исходный код выглядит следующим образом:
@HotSpotIntrinsicCandidate
public static void onSpinWait() {}
Что это на самом деле означает, что этот метод в основном является NO-OP, пока он не достигнет c2 compiler
в JIT
, согласно this
Ответ 4
В качестве реального примера, скажем, вы хотите реализовать асинхронное ведение журнала, когда потоки, которые хотят что-то записать, не хотят ждать, пока их сообщение журнала будет "опубликовано" (скажем, записано в файл), до тех пор, пока это в конечном счете делает (потому что у них есть реальная работа, чтобы сделать.)
Producer(s):
concurrentQueue.push("Log my message")
И скажем, вы решаете иметь отдельный потребительский поток, который несет полную ответственность за фактическую запись сообщений журнала в файл:
(Single)Consumer
while (concurrentQueue.isEmpty())
{
//what should I do?
}
writeToFile(concurrentQueue.popHead());
//loop
Вопрос в том, что делать внутри блока while? Java не предоставила идеальных решений: вы могли бы выполнять Thread.sleep(), но как долго и в таком тяжелом весе; или Thread.yield(), но это не указано, или вы можете использовать блокировку или мьютекс *, но это часто слишком тяжеловесно и также замедляет работу производителей (и умаляет заявленную цель асинхронного ведения журнала).
Что вы действительно хотите, так это сказать среде выполнения: "Я ожидаю, что не буду ждать слишком долго, но я бы хотел свести к минимуму любые накладные расходы на ожидание/отрицательные эффекты для других потоков". Это где Thread.onSpinWait() входит.
Как указывалось выше, на платформах, которые его поддерживают (например, x86), onSpinWait() встроен в инструкцию PAUSE, которая даст вам те преимущества, которые вы хотите. Так:
(Single)Consumer
while (concurrentQueue.isEmpty())
{
Thread.onSpinWait();
}
writeToFile(concurrentQueue.popHead());
//loop
Опытным путем было показано, что это может улучшить задержку циклов стиля "ожидание занято".
Я также хочу уточнить, что это не просто полезно для реализации "спин-блокировок" (хотя это определенно полезно в таких обстоятельствах); приведенный выше код не требует какой-либо блокировки (вращения или иного).
Если вы хотите попасть в сорняки, вы не можете сделать лучше, чем спецификации Intel
* Для ясности, JVM невероятно умна в попытке минимизировать стоимость мьютексов, и первоначально будет использовать легкие блокировки, но это другое обсуждение.