О четырех состояниях блокировки и процессе обновления блокировки

Java

Введение

Всего существует четыре состояния блокировки, а уровни от низкого до высокого:Без блокировки, Блокировка смещения, Легкая блокировка, Тяжелая блокировка, что представляют собой эти четыре состояния блокировки и почему происходит укрупнение блокировки? На самом деле, до JDK 1.6,синхронизированный по-прежнему тяжеловесный замок, является относительно неэффективной блокировкой, но после JDK 1.6, чтобы повысить эффективность получения и освобождения блокировки, JVMsynchronized) был оптимизирован для введенияБлокировка смещения и облегченная блокировка, с тех пор существует четыре состояния блокировки (нет блокировки, предвзятая блокировка, облегченная блокировка, тяжелая блокировка),И четыре состояния будут постепенно улучшаться с конкуренцией, и это необратимый процесс, то есть его нельзя понизить, то есть можно выполнить только обновление блокировки (с низкого уровня на высокий уровень) и понижение блокировки ( высокий уровень на низкий уровень) не может быть выполнено., что означает, что смещенная блокировка не может быть понижена до смещенной блокировки после обновления до облегченной блокировки. Целью этой стратегии укрупнения блокировки, а не понижения уровня, является повышение эффективности получения и снятия блокировок.

Два, четыре состояния замка

существуетsynchronizedПервоначальная реализация была "Блокировка или пробуждение потока Java требует от операционной системы переключения состояния ЦП. Это переключение состояния требует времени процессора, и если содержимое блока синхронизированного кода слишком простое, это переключение может занять больше времени, чем время выполнения пользовательского кода»., этот способsynchronizedПервоначальный способ достижения синхронизации также является тем, что разработчики критиковали в начале, что также было до JDK6.synchronizedИз-за неэффективности в JDK6 были введены «предвзятые блокировки» и «облегченные блокировки», чтобы снизить потребление производительности, вызванное получением и освобождением блокировок.

Поэтому в настоящее время существует четыре типа состояний блокировки, от низкого до высокого:Без замка, косой замок, легкий замок, тяжелый замок, состояние блокировки можно только повысить, но не понизить

как показано на рисунке:

在这里插入图片描述

3. Идея и характеристики состояния блокировки

статус блокировки хранить содержимое бит флага
нет замка Хэш-код объекта, возраст генерации объекта, является ли он предвзятой блокировкой (0) 01
Блокировка смещения Идентификатор предвзятого потока, предвзятая отметка времени, возраст создания объекта, является ли это предвзятой блокировкой (1) 01
Легкий замок указатель на запись блокировки в стеке 00
тяжелый замок указатель на мьютекс 11

Четыре, сравнение блокировки

Замок преимущество недостаток Применимая сцена
Блокировка смещения Блокировка и разблокировка не требуют дополнительного потребления, а разрыв составляет всего наносекунду по сравнению с выполнением асинхронных методов. Если между потоками существует конкуренция за блокировку, это приведет к дополнительному потреблению отзыва блокировки. Подходит для сценариев, когда только один поток обращается к синхронизированным блокам.
Легкий замок Конкурирующие потоки не будут блокироваться, что повышает скорость отклика программы. Использование spin будет потреблять ресурсы ЦП, если нет доступных конкурирующих потоков. Погоня за скоростью отклика, скорость выполнения синхронизированного блока очень высока
тяжелый замок Конкуренция потоков не использует вращение и не потребляет ЦП Блокировка потока и медленное время отклика В погоне за пропускной способностью синхронизированные блоки выполняются медленнее.

Пять, синхронизированный замок

synchronizedИспользуемая блокировка хранится в заголовке объекта Java, так что же такое заголовок объекта?

5.1 Заголовок объекта Java

Возьмем в качестве примера виртуальную машину Hotspot.Заголовок объекта Hopspot в основном включает две части данных:Mark Word(标记字段) 和 Klass Pointer(类型指针)

Mark Word: HashCode, возраст генерации и информация о флаге блокировки объекта сохраняются по умолчанию. Вся эта информация представляет собой данные, не связанные с определением самого объекта, поэтому Mark Word разработан как нефиксированная структура данных, чтобы хранить как можно больше данных в очень небольшом пространстве памяти. Он будет повторно использовать свое собственное пространство для хранения в соответствии с состоянием объекта, то есть данные, хранящиеся в Mark Word, будут изменяться при изменении флага блокировки во время работы.

Klass Point: указатель объекта на его метаданные класса, виртуальная машина использует этот указатель, чтобы определить, экземпляром какого класса является объект.

Из вышеизложенного мы знаем,synchronizedИспользуемая блокировка хранится в заголовке объекта Java, так где же находится заголовок объекта? ответ:Хранится в слове маркировки заголовка объекта объекта блокировки, так как же именно выглядит MarkWord в заголовке объекта и что именно он хранит?

В 64-битной виртуальной машине:

在这里插入图片描述
В 32-битной виртуальной машине:
在这里插入图片描述

Давайте возьмем в качестве примера 32-битную виртуальную машину, чтобы увидеть, как распределяются байты ее Mark Word.

нет замка: Заголовок объекта открывает 25 бит пространства для хранения хэш-кода объекта, 4 бита используются для хранения возраста создания объекта, 1 бит используется для хранения бита идентификации блокировки, а 2 бита используются для хранения бит идентификации блокировки как 01

Блокировка смещения:В смещенной блокировке деление более тонкое, или открывается 25-битное пространство, из которых 23 бита используются для хранения идентификатора потока, 2 бита используются для хранения эпохи, 4 бита используются для хранения возраста генерации объекта, 1 бит хранится независимо от того, является ли идентификатор смещенной блокировки, 0 означает отсутствие блокировки, 1 означает смещенную блокировку, бит идентификации блокировки по-прежнему равен 01

Легкий замок: Непосредственно открыть 30 бит пространства в облегченной блокировке для хранения указателя на запись блокировки в стеке и 2 бита для хранения бита флага блокировки, бит флага которого равен 00.

Тяжелый замок:В тяжеловесной и облегченной блокировке 30-битное пространство используется для хранения указателя на тяжеловесную блокировку, а 2-битное пространство используется для хранения идентификационного бита блокировки, который равен 11.

Флаги сборщика мусора:30-битное пространство памяти открыто, но не занято, а 2-битное пространство для хранения флага блокировки равно 11.

Флаги блокировки блокировки без блокировки и блокировки со смещением равны 01, но первый 1 бит различает, является ли это состоянием без блокировки или состоянием смещенной блокировки.

Что касается выделения памяти, мы можем сделать это в openJDK в git.markOop.hppКак можно заметить:

public:
  // Constants
  enum { age_bits                 = 4,
         lock_bits                = 2,
         biased_lock_bits         = 1,
         max_hash_bits            = BitsPerWord - age_bits - lock_bits - biased_lock_bits,
         hash_bits                = max_hash_bits > 31 ? 31 : max_hash_bits,
         cms_bits                 = LP64_ONLY(1) NOT_LP64(0),
         epoch_bits               = 2
  };
  • возраст_бит:Это идентификатор рециркуляции поколений, о котором мы говорим, который занимает 4 байта.
  • lock_bits:Это флаговый бит блокировки, занимающий 2 байта.
  • biad_lock_bits:Идентификация того, смещена ли блокировка, занимающая 1 байт
  • max_hash_bits:Это количество байтов, занимаемых хэш-кодом для безблокировочного расчета.Если это 32-битная виртуальная машина, то 32 - 4 - 2 -1 = 25 байт. Если это 64-битная виртуальная машина, 64 - 4 - 2 - 1 = 57 байт, но 25 байт будут неиспользованными, поэтому 64-битный хэш-код занимает 31 байт.
  • hash_bits:Для 64-битных виртуальных машин, если максимальное количество байт больше 31, берется 31, в противном случае берется реальное количество байт.
  • cms_bits:Если это не 64-битная виртуальная машина, она будет занимать 0 байт, а если это 64-битная виртуальная машина, то она будет занимать 1 байт.
  • эпохи_биты:Это размер байтов, занятых эпохой, 2 байта.

5.2 Monitor

Монитор можно понимать как инструмент синхронизации или механизм синхронизации, обычно описываемый как объект. Каждый объект Java имеет невидимую блокировку, называемую внутренней блокировкой или блокировкой монитора.

Монитор — это структура данных, приватная для потока, у каждого потока есть список доступных записей монитора, а также есть глобальный доступный список. Каждый заблокированный объект будет связан с монитором, и в мониторе есть поле «Владелец» для хранения уникального идентификатора потока, которому принадлежит блокировка, указывающего, что блокировка занята этим потоком.

SynchronizedОн реализуется через внутренний объект, называемый блокировкой монитора (monitor), а суть блокировки монитора реализуется за счет использования Mutex Lock (блокировка взаимного исключения) базовой операционной системы. Операционная система должна переключаться из состояния пользователя в состояние ядра для переключения между потоками.Эта стоимость очень высока, а переход между состояниями занимает относительно много времени, поэтому Synchronized неэффективен. Поэтому такой вид блокировки, который зависит от реализации Mutex Lock в операционной системе, называется тяжеловесной блокировкой.

С конкуренцией замков замки могут быть модернизированы со смещенных замков до легких замков, а затем модернизированы до тяжелых замков (но модернизация замков является односторонней, то есть ее можно модернизировать только от низкого до высокого, и там не будет замков даунгрейда). Предвзятые блокировки и облегченные блокировки включены по умолчанию в JDK 1.6. Мы также можем отключить предвзятые блокировки с помощью -XX:-UseBiasedLocking=false.

Шесть, классификация замков

6.2 Без блокировки

Без блокировки означает, что ресурс не заблокирован, все потоки могут получать доступ и изменять один и тот же ресурс, но только один поток может успешно изменять его одновременно.

Характеристика блокировки без блокировки заключается в том, что операция модификации будет выполняться в цикле, а поток будет постоянно пытаться изменить общий ресурс. Если конфликта нет, модификация завершается успешно и завершается, в противном случае цикл продолжит попытки. Если несколько потоков изменяют одно и то же значение, должен быть один поток, который может успешно изменить значение, а другие потоки, которым не удастся изменить значение, будут продолжать повторять попытки до тех пор, пока изменение не будет успешным.

6.3 Блокировка смещения

Когда блок синхронизированного кода выполняется в первый раз, объект блокировки становится смещенной блокировкой (измените флаг блокировки в заголовке объекта через CAS), что буквально означает «смещен к первому потоку, который его получает». После выполнения блока синхронизированного кода поток не освобождает активную блокировку смещения. Когда блок кода синхронизации будет достигнут во второй раз, поток определит, является ли поток, удерживающий блокировку, самим собой (идентификатор потока, удерживающего блокировку, также находится в заголовке объекта), и если это так, он будет выполняться нормально. Так как ранее блокировка не снималась, повторно блокировать здесь не нужно. Если есть только один поток, использующий блокировку все время, очевидно, что смещенные блокировки почти не имеют дополнительных накладных расходов, а производительность чрезвычайно высока.

Предвзятая блокировка означает, что когда часть кода синхронизации всегда используется одним и тем же потоком, т. е. когда нет конкуренции между несколькими потоками, поток будет автоматически получать блокировку при последующих обращениях, тем самым уменьшая потребление, вызванное получением блокировки. , то есть улучшить производительность.

Когда поток получает доступ к блоку синхронизированного кода и получает блокировку, идентификатор потока смещения блокировки сохраняется в Mark Word. Когда поток входит в синхронизированный блок и выходит из него, он больше не блокируется и не разблокируется с помощью операций CAS, а определяет наличие смещенной блокировки, указывающей на текущий поток, хранящийся в Mark Word. Получение и освобождение облегченных блокировок зависит от нескольких атомарных инструкций CAS, в то время как смещенные блокировки должны полагаться только на одну атомарную инструкцию CAS при замене ThreadID.

Смещенная блокировка снимает блокировку только тогда, когда другие потоки пытаются конкурировать за смещенную блокировку, и поток не будет активно снимать смещенную блокировку.

Что касается отзыва предвзятой блокировки, необходимо дождаться глобальной точки безопасности, то есть, когда в определенный момент времени нет исполняемого байт-кода, он сначала приостановит поток, которому принадлежит предвзятая блокировка, а затем определит, объект блокировки заблокирован. Если поток не активен, заголовок объекта устанавливается в состояние отсутствия блокировки, блокировка смещения отменяется и восстанавливается состояние блокировки без блокировки (флаг 01) или упрощенная блокировка (флаг 00).

6.4 Облегченные блокировки (спин-блокировки)

在这里插入图片描述

Облегченная блокировка означает, что когда блокировка является смещенной блокировкой, к ней обращается другой поток.В это время смещенная блокировка будет обновлена ​​до облегченной блокировки, а другие потоки будут вращаться через спин (см. конец статьи). для введения вращения) При попытке получить блокировку поток не будет блокироваться, что повышает производительность.

Приобретение облегченных замков в основном состоит из двух ситуаций: ① Когда функция блокировки отклонения отключена; ② Смещенная блокировка модернизирована до облегченной блокировки из-за конкуренции со смещенной блокировкой несколькими потоками.

Как только второй поток присоединяется к соревнованию замков, смещенная блокировка обновляется до облегченной блокировки (спин-блокировки). Здесь поясню, что такое конкуренция за блокировку: если несколько потоков по очереди получают блокировку, но каждый раз блокировка получается гладко и блокировка не происходит, то конкуренции за блокировку нет. Конкуренция за блокировку возникает только тогда, когда поток пытается получить блокировку и обнаруживает, что блокировка уже занята и может только ждать ее освобождения.

Продолжите соревнование замков в облегченном состоянии блокировки, и поток, который не захватит блокировку, будет вращаться, то есть он будет продолжать выполнять цикл, чтобы определить, можно ли успешно получить блокировку. Операция получения блокировки фактически заключается в изменении флага блокировки в заголовке объекта через CAS. Сначала сравните, «освобожден» ли текущий флаг блокировки, и если да, установите его в «заблокировано», сравнение и установка являются атомарными. Это даже захватывает блокировку, а затем поток изменяет информацию о текущем держателе блокировки на себя.

Долгосрочная операция вращения очень ресурсоемка.Один поток удерживает блокировку, а другие потоки могут только потреблять ЦП на месте и не могут выполнять какие-либо эффективные задачи.Это явление называется ожиданием занятости. Если несколько потоков используют блокировку, но нет конкуренции блокировок или существует очень небольшая конкуренция блокировок, тогда синхронизация использует облегченную блокировку, чтобы разрешить кратковременную занятость. Это компромиссная идея, короткое время занято ожиданием накладных расходов на переключение потоков между пользовательским режимом и режимом ядра.

6.4 Тяжелый замок

Тяжеловесные блокировки Очевидно, что это занятое ожидание ограничено (есть счетчик для записи количества спинов, по умолчанию разрешено 10 циклов, которые можно изменить параметрами виртуальной машины). Если конкуренция между блокировками серьезная, поток, достигший максимального количества вращений, обновит облегченную блокировку до тяжелой блокировки (CAS по-прежнему изменяет флаг блокировки, но не изменяет идентификатор потока, удерживающего блокировку). Когда последующий поток пытается получить блокировку и обнаруживает, что занятая блокировка является тяжеловесной, он напрямую приостанавливает себя (а не занят ожиданием) и ожидает пробуждения в будущем.

Тяжеловесная блокировка означает, что когда один поток получает блокировку, все остальные потоки, ожидающие ее получения, будут заблокированы.

Короче говоря, все управление передается операционной системе, а операционная система отвечает за планирование между потоками и изменение состояния потоков. Таким образом, будет происходить частое переключение рабочего состояния потока, приостановка и пробуждение потока, что потребляет большое количество системных ресурсов.

V. Резюме

В статье описываются четыре состояния блокировки и пошаговое обновление блокировки.Если в статье есть какие-либо непонятки или проблемы, вы можете указать их и сообщить об этом в области комментариев ниже.Спасибо.