Урегулируйте у детей, это будет долго.
Первое, что нужно обсудить CAS ( C ompare A nd S wap) это не механизм синхронизации.Это атомарная операция, которая позволяет нам обновлять значение в основной памяти, проверяя одновременность, если это значение не изменилось (или мы ожидаем, что оно будет).Здесь нет блокировки.Хотя они используются некоторыми примитивами синхронизации (семафоры, мьютексы).Давайте посмотрим на следующий пример:
a = 1;
--------------------------------
Thread 1 | Thread 2
b = 1 + a | b = 2 + a
cas(*a, 1, b ) | cas(*a, 1, b )
Теперь одна из CAS-сбоев выйдет из строя, и я имею в виду, что она вернет false.Другой вернет true, и значение, которое представляет указатель * a, будет обновлено новым значением.Если бы мы не использовали CAS, а вместо этого просто обновили значение, например, так:
a = 1;
--------------------------------
Thread 1 | Thread 2
b = 1 + a | b = 2 + a
a = b | a = b
В конце этого вычисления значение a может быть 2 или 3, и оба потока завершатся счастливо, не зная, какое значение былосохранено в.Это то, что называется гонкой данных, и CAS - способ решить эту проблему.
Существование CAS позволяет нам писать некоторые алгоритмы без блокировки (синхронизация не требуется), например, коллекции в java.util.concurrent.пакет, который не нужно синхронизировать, для одновременного доступа.
Теперь я упомянул, что CAS используется для реализации синхронизации.Вот почему стоимость приобретения блокировки и выполнения CAS практически одинакова (если нет разногласий !!!!) И в этой отправке вы получаете аппаратную поддержку для синхронизированного ключевого слова.
synchronized(this){
n = n + 1;
}
AtomicLong al = new AtomicLong();
al.updateAndGet( n -> n + 1)
Снижение производительности, которое вы можете получить при использовании CAS против синхронизации, происходит из-за сбоя CAS, вы можете просто повторить попытку, в то время как использование синхронизации может привести к тому, что поток перейдет в спящий режим.Переход к кроличьей норе переключателей контекста (это может или не может произойти :) в зависимости от ОС).
Теперь для notify(), notifyAll() and wait()
.Вызывает напрямую в планировщик потоков, который является частью ОС.Планировщик имеет две очереди Очередь ожидания и Очередь выполнения .Когда вы вызываете ожидание потока, этот поток помещается в wq и сидит там, пока не получит уведомление и не поместит его в rq, чтобы он был выполнен как можно скорее.
В Java существует две основные синхронизации потоков, одна из которых (wait (), notify ()) называется кооперация , а другая - через блокировки, называемые взаимное исключение (мьютекс),И это, как правило, для параллельных треков, чтобы делать мысли сразу.
Теперь я не знаю, как была выполнена синхронизация до Java 5. Но теперь у вас есть 2 способа синхронизации с использованием объекта (один из возможныхстарый другой новый).
Смещенная блокировка.Идентификатор потока помещается в заголовок объекта , а затем, когда тот же конкретный поток хочет заблокировать, разблокировать этот объект, эта операция нам ничего не стоит.Вот почему, если в нашем приложении много неконтролируемых блокировок, это может значительно повысить производительность.Как мы можем избежать второго пути:
(это, вероятно, старый) с использованием monitorenter/monitorexit
.Это инструкции байт-кода.Они размещаются на входе и выходе оператора synchronize {...}
.Вот где идентичность объекта становится актуальной.Как это становится частью информации о блокировке.
ОК, что это.Я знаю, что не ответил на вопрос полностью.Тема такая сложная и такая сложная.Глава 17 «Спецификации языка Java» под названием: «Модель памяти Java», вероятно, единственная, которую не могут прочитать обычные программисты (возможно, динамическая диспетчеризация также подпадает под эту категорию :)).Я надеюсь, что по крайней мере вы сможете гуглить правильные слова.
Пара ссылок: https://www.artima.com/insidejvm/ed2/threadsynchP.html (monitorenter / monitorexit, пояснение)
https://www.ibm.com/developerworks/library/j-jtp10185/index.html (как оптимизируются блокировки внутри jvm)