1. тяжелый замок
В предыдущей статье мы представили использование синхронизации и его принципов ее реализации. Теперь мы должны знать, что синхронизированный реализуется через блокировку монитора внутри объекта. Тем не менее, блокировка монитора по существу реализована, полагаясь на блокировку Mutex основной операционной системы. Операционная система должна переключаться между потоками от состояния пользователя на основное состояние. Это очень дорого, и конверсия между государствами занимает относительно много времени. Вот почему синхронизированный неэффективен. Поэтому мы называем эту блокировку, которая опирается на реализацию операционной системы Mutex Lock «Heavy Weely Lock». Ядро различных оптимизаций, сделанных для синхронизации в JDK, заключается в сокращении использования этого тяжелого веса. После JDK1.6, чтобы уменьшить потребление производительности, вызванное получением и выпуском замков и повышения производительности, были введены «легкие замки» и «смещенные замки».
2. Легкий замок
Существует четыре типа состояний блокировки: состояние без блокировки, смещенная блокировка, легкий замок и тяжелый замок. Благодаря конкуренции за замки можно обновить с смещенных замков до легких замков, а затем модернизированных замков в тяжелом весе (но обновление замков составляет одностороннее, что означает, что они могут обновляться только с низкого до высокого уровня, и не будет деградации замков). В JDK 1.6 блокировка смещения и легкий блокировка включены по умолчанию. Мы также можем отключить блокировку смещения от -xx: -Usebiasedlocking. Состояние блокировки сохраняется в файле заголовка объекта, взяв 32-битный JDK в качестве примера:
Статус блокировки | 25 бит | 4 -бит | 1 бит | 2 бит | ||
23 -битный | 2 бит | Это предвзятый замок? | Блокировать флаг бит | |||
Легкий замок | Указатель на записи блокировки в стеке | 00 | ||||
Тяжелый замок | Указатель на Mutex (Leaffice Lock) | 10 | ||||
Теги GC | нулевой | 11 | ||||
Положительный замок | Идентификатор потока | Эпоха | Испытуемые возраст | 1 | 01 | |
Беззамажный | хешкод объекта | Испытуемые возраст | 0 | 01 | ||
«Легкий» относится к традиционным замкам, которые используют операционную систему Mutexes. Тем не менее, важно сначала подчеркнуть, что легкие замки не используются для замены замков в тяжелом весе. Его первоначальное намерение состоит в том, чтобы сократить потребление производительности, генерируемое использованием традиционных тяжелых замков без многопоточной конкуренции. Прежде чем объяснить процесс выполнения легких замков, мы сначала понимаем, что сценарии, адаптированные к легким замкам, - это тот случай, когда потоки попеременно выполняют синхронные блоки. Если доступ к такой же замке доступен одновременно, легкий замок будет расширяться в тяжелый замок.
1. Процесс блокировки легкого блокировки
(1) Когда код попадает в блок синхронизации, если состояние блокировки объекта синхронизации не содержат блокировки (флаг блокировки-это состояние «01», будь то смещенное блокировка «0»), виртуальная машина сначала создаст пространство, называемое записи блокировки в кадре стека текущего потока для хранения текущей копии текущего знака объекта блокировки, которое официально называется смещенным отмеченным словом. В настоящее время состояние стека и заголовка объекта показано на рисунке 2.1.
(2) Скопируйте известное слово в заголовке объекта и скопируйте его в запись блокировки.
(3) После успешной копии виртуальная машина будет использовать операции CAS, чтобы попытаться обновить знак отметки объекта на указатель для блокировки записи, и указать указатель владельца в записи блокировки на объект Mark Word. Если обновление является успешным, тогда выполните шаг (3), в противном случае выполните шаг (4).
(4) Если это действие обновления является успешным, то поток имеет блокировку объекта, и флаг блокировки слова «отметка объекта» установлен на «00», что означает, что объект находится в легком заблокированном состоянии. В это время состояние стека резьбов и головка объекта показано на рисунке 2.2.
(5) Если эта операция обновления не выполняется, виртуальная машина сначала проверит, указывает ли слово «отметка объекта» на кадр стека текущего потока. Если это так, это означает, что текущий поток уже имеет блокировку объекта, а затем он может непосредственно ввести блок синхронизации, чтобы продолжить выполнение. В противном случае несколько потоков конкурируют за замки, а легкий замок будет расширяться в тяжелый замок, а значение состояния флага блокировки станет «10». Указатель на тяжелый замок (Mutex) хранится в Mark Word, а поток, ожидающий блокировки, также войдет в состояние блокировки. Текущая потока пытается использовать Spin для получения блокировки. Спин должен избежать блокировки нити и использовать петлю, чтобы приобрести блокировку.
Рисунок 2.1. Состояние стека и объекта перед легкой работой CAS CAS
Рисунок 2.2. Состояние стека и объекта после легкой работы CAS CAS
2. Процесс разблокировки легкого блокировки:
(1) Попробуйте заменить объект с перемещенным знаком Word, скопированный в потоке с помощью работы CAS.
(2) Если замена будет успешной, весь процесс синхронизации будет завершен.
(3) Если замена не удается, это означает, что другие потоки попытались приобрести блокировку (замок был расширен в настоящее время), то подвесная нить должна быть пробуждена при выпуске блокировки.
3. Положительный замок
Внедрение блокировки BIAS состоит в том, чтобы минимизировать ненужные легкие пути выполнения блокировки без многопоточных конкурентов, поскольку получение и выпуск легких замков зависят от множества атомных инструкций CAS, в то время как блокировки смещения должны полагаться только на одну атомную инструкцию CAS. Атомных инструкций CAS). Как упоминалось выше, легкие блокировки используются для повышения производительности, когда потоки попеременно выполняют синхронные блоки, в то время как смещенные блокировки используются для дальнейшего повышения производительности, когда только один поток выполняет синхронные блоки.
1. Процесс сбора предвзятого блокировки:
(1) Доступ к тому, установлен ли флаг блокировки смещения в Mark Word, и является ли флаг блокировки 01 - подтвердите, что это смещаемое состояние.
(2) Если это смещаемое состояние, проверьте, указывает ли идентификатор потока на текущий поток. Если это так, введите шаг (5), в противном случае введите шаг (3).
(3) Если идентификатор потока не указывает на текущий поток, то блокировка будет конкурировать с помощью операции CAS. Если конкуренция успешна, установите идентификатор потока в Mark Word на текущий идентификатор потока и выполнить (5); Если конкуренция не удается, выполнить (4).
(4) Если CAS не сможет приобрести блокировку смещения, это означает, что есть конкуренция. Когда глобальная SafePoint достигается, резьба, которая получает блокировку смещения, приостановлен. Замок смещения обновляется до легкой блокировки, а поток заблокирован в SafePoint продолжает выполнять код синхронизации.
(5) Выполните код синхронизации.
2. Выпуск предвзятого блокировки:
Отмена предвзятого замка упоминается на четвертом шаге выше. Предвзятая блокировка отпустит блокировку только тогда, когда другие потоки попытаются конкурировать за смещенную блокировку, а поток не будет активно отключать смещенную блокировку. Отмена предвзятого блокировки требует ожидания глобальной точки безопасности (на данный момент не выполняется байт -код). Сначала он сделает паузу с смещенной блокировкой, определит, находится ли объект блокировки в заблокированном состоянии, а затем вернется к разблокированному (бит флага «01») или легкий замок (бит флага «00») после устранения смещенного блокировки.
3. Преобразование между тяжелым замком, легким замком и блокировкой смещения
Рисунок 2.3 Диаграмма преобразования трех
Эта картина в основном является кратким изложением вышеуказанного контента. Если у вас хорошее понимание приведенного выше контента, картинка должна быть легко понять.
4. Другие оптимизации
1. Адаптивное спиннинг: от процесса получения легких замков мы знаем, что, когда нить не выполняет работу CAS во время получения легких замков, необходимо получить замок в тяжелом весе через спин. Проблема в том, что SPIN требует потребления процессора. Если замок не может быть получен, нить будет напрасным в состоянии спинового состояния и напрасным отходом ресурсов процессора. Самый простой способ решить эту проблему - указать количество спинов, например, позволить ей циклироваться 10 раз и ввести состояние блокировки, если блокировка не получена. Но JDK принимает более разумный подход - адаптивный вращение. Проще говоря, если поток преуспевает, количество спинов будет более в следующий раз, и если спин не удастся, количество спинов будет уменьшено.
2. Заблокированное уравновешивание: концепция сковорочности блокировки должна быть проще для понимания, чтобы объединить операции блокировки и разблокировать, соединенные вместе, в один раз, расширяя несколько непрерывных замков в блокировку с большим диапазоном. Например:
пакет com.paddx.test.string; public class stringbuffertest {stringBuffer stringBuffer = new StringBuffer (); public void append () {stringbuffer.append ("a"); stringbuffer.append ("b"); stringbuffer.append ("c"); }}Здесь каждый раз, когда вы называете метод stringbuffer.append, требуется блокировка и разблокировка. Если виртуальная машина обнаружит серию операций блокировки и разблокировки на одном и том же объекте, она объединит ее в больший диапазон операций блокировки и разблокировки, то есть блокировка выполняется на первом методе добавления, а разблокировка выполняется после завершения последнего метода добавления.
3. Устранение блокировки: устранение блокировки означает удаление ненужных операций блокировки. Согласно технологии Escode, если определено, что кусок кода и данные на куче не выберут из текущего потока, то можно считать, что этот кусок кода безопасен для потока, и нет необходимости блокировать его. Посмотрите на следующую программу:
пакет com.paddx.test.concurrent; public class synchronizedtest02 {public static void main (string [] args) {synchronizedTest02 test02 = new SynchronizedTest02 (); // запустить разминку для (int i = 0; i <10000; i ++) {i ++; } long Start = System.currentTimeMillis (); for (int i = 0; i <10000000000; i ++) {test02.append ("abc", "def"); } System.out.println ("time =" + (System.currentTimeMillis () - Start)); } public void Append (String Str1, String Str2) {StringBuffer sb = new StringBuffer (); sb.append (str1) .append (str2); }}Хотя приложение StringBuffer является синхронным методом, StringBuffer в этой программе принадлежит к локальной переменной и не выходит из метода. Следовательно, этот процесс фактически безопасен для резьбы и может устранить блокировку. Вот результат моего местного исполнения:
Чтобы минимизировать влияние других факторов, здесь отключено блокировка смещения (-xx: -себиаседлокинг). Благодаря вышеупомянутой программе видно, что производительность была значительно улучшена после устранения блокировки.
Примечание. Результаты выполнения могут отличаться между различными версиями JDK. Версия JDK, которую я использую здесь, - 1.6.
5. Резюме
Эта статья посвящена оптимизации синхронизации в JDK, таких как легкие замки и смещенные замки, но эти два замка не совсем без недостатков. Например, когда конкуренция является жесткой, она не только не может повысить эффективность, но и снизить эффективность, поскольку существует дополнительный процесс обновления блокировки. В настоящее время необходимо отключить предвзятые замки через -xx: -usebiasedlocking. Вот сравнение этих замков:
Замок | преимущество | недостаток | Применимые сценарии |
Положительный замок | Блокировка и разблокировка не требует дополнительного потребления, и существует наносекундный разрыв по сравнению с выполнением асинхронного метода. | Если между потоками существует конкуренция за блокировку, это приведет к дополнительному потреблению отзыва блокировки. | Подходит для сценариев, где только один поток доступ к синхронному блоку. |
Легкий замок | Конкурирующие потоки не будут блокировать, что улучшает скорость отклика программы. | Если нить, которая никогда не получает конкуренцию за блокировки, будет потреблять процессор. | Преследование времени ответа. Синхронное выполнение блоков очень быстро. |
Тяжелый замок | Соревнование по тему не использует спин и не потребляет процессор. | Блокировка потока, время медленного отклика. | Пропускная пропускная способность. Скорость выполнения блока синхронизации является относительно длинной. |