Подтвердить что ты не робот

Метод onSpinWait() класса Thread

В то время как обучение Java 9 я наткнулся на новый метод Thread класса, называемого onSpinWait​. Согласно Javadocs, этот метод используется для этого:

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

Может ли кто-нибудь помочь мне понять этот метод, приводя реальный пример?

4b9b3361

Ответ 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 невероятно умна в попытке минимизировать стоимость мьютексов, и первоначально будет использовать легкие блокировки, но это другое обсуждение.