Помните, что когда вы впервые начали изучать Java, когда вы столкнулись с многопоточной ситуацией, она была синхронизирована. Для нас в то время синхронизация была такой волшебной и могущественной. Мы дали ему имя «синхронизация», и это также стало нашим хорошим лекарством для решения ситуации с многопоточностью. Однако с углублением обучения мы знаем, что synchronized — это тяжеловесная блокировка, по сравнению с Lock она будет настолько громоздкой, что мы думаем, что она не так эффективна. С Javs SE 1.6 различные оптимизации для синхронизированных, синхронизированных не будут казаться такими тяжелыми.
Synchronized может гарантировать, что при выполнении метода или блока кода только один метод одновременно входит в критическую секцию, а также может обеспечить видимость в памяти общих переменных.
1. Принцип реализации
Каждый объект в Java можно использовать в качестве блокировки, что является основой для синхронизированного для достижения синхронизации:
- Обычный метод синхронизации, замком является текущий экземпляр объекта.
- Метод статической синхронизации блокирует объект класса текущего класса.
- Синхронизированный кодовый блок, замок представляет собой объект, настроенный в синхронизированных скобках.
Когда поток обращается к синхронизированному блоку кода, он должен получить блокировку перед выполнением синхронизированного кода и снять блокировку при выходе или возникновении исключения. Мы будем задаваться вопросом, где существует замок? Какая информация хранится в замке?
Определение спецификации JVM: JVM реализует синхронизацию методов и синхронизацию блоков кода на основе входа и выхода из объектов Monitor:
- Синхронизация блоков кода: реализована с помощью директив monitorenter и monitorexit.
- Синхронизация метода: используйте другой метод, но также используйте эти две инструкции. Отличается только конкретное выражение
public class SynchronizedTest {
public synchronized void test1() {
}
public void test2() {
synchronized (this) {
}
}
}
Используйте инструмент javap -verbose SynchronizedTest.class, чтобы просмотреть информацию о сгенерированном файле класса и проанализировать синхронизированную реализацию:
Опустите часть кода следующим образом:
{
public com.zero.test.SynchronizedTest();
.....
public synchronized void test1();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 5: 0
public void test2();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_1
5: monitorexit
6: goto 14
9: astore_2
10: aload_1
11: monitorexit
12: aload_2
13: athrow
14: return
......
}
Из приведенного выше видно, что синхронизированный блок кода реализован с помощью инструкций monitorenter и monitorexit, а синхронизированный метод реализован с помощью ACCSYNCHRONIZED в модификаторе метода.Так или иначе, суть в том, чтобы монитор объекта (монитор) получить.
Прежде чем мы продолжим углубленный анализ, мы должны сначала понять две концепции:Заголовок объекта Java, Monitor.
1.1, заголовок объекта Java
Блокировка, используемая синхронизацией, хранится в заголовке объекта Java.Заголовок объекта виртуальной машины Hotspot в основном включает две части данных: Mark Word (поле метки) и Klass Pointer (указатель типа). Среди них Klass Point — это указатель, который объект указывает на метаданные своего класса.Виртуальная машина использует этот указатель, чтобы определить, к какому экземпляру класса относится объект.Mark Word используется для хранения данных времени выполнения самого объекта.Он реализует облегченные блокировки и ключ блокировки смещения.
Mark Word
Слово Mark в заголовке объекта Java используется для хранения данных времени выполнения самого объекта, таких как хэш-код (HashCode), возраст генерации GC, флаг состояния блокировки, блокировка, удерживаемая потоком, смещенный идентификатор потока, смещенная отметка времени и т. д. Заголовок объекта Java обычно занимает два машинных кода (в 32-битной виртуальной машине 1 машинный код равен 4 байтам, то есть 32 битам), но если объект имеет тип массива, требуются три машинных кода, поскольку Виртуальная машина JVM может Размер объекта Java определяется информацией о метаданных объекта Java, но размер массива не может быть подтвержден из метаданных массива, поэтому для записи длины массива используется блок. , структура хранения Mark Word для 32-разрядной JVM по умолчанию выглядит следующим образом:
статус блокировки | 25bit | 4bit | Является ли 1bit предвзятой блокировкой? | 2-битный флаг блокировки |
---|---|---|---|---|
безблокировочное состояние | объект hashCode | Генерационный возраст субъекта | 0 | 01 |
Во время работы данные, хранящиеся в Mark Word, изменяются при изменении бита флага блокировки.
Monitor
Мы можем понимать его как инструмент синхронизации или как механизм синхронизации, который обычно описывается как объект.
Как и все является объектом, все объекты Java рождаются мониторами, и каждый объект Java потенциально может стать монитором, потому что в дизайне Java каждый объект Java выходит из чрева с невидимым замком. блокировки или блокировки монитора.
2. Синхронизируйте оптимизацию производительности
Мы знаем, что синхронизированный — это тяжеловесный замок, который не очень эффективен, в то же время это понятие всегда существовало в нашем сознании, но вБыли внесены различные оптимизации в реализацию синхронизации в JDK 1.6, что сделало ее менее тяжелой., какие есть методы оптимизации?
2.1, оптимизация блокировки
В JDK1.6 реализовано большое количество оптимизаций реализации блокировок, таких как спин-блокировки, адаптивные спин-блокировки, устранение блокировок, огрубление блокировок, предвзятые блокировки, облегченные блокировки и другие технологии для снижения накладных расходов на операции блокировок.
Блокировки в основном существуют в четырех состояниях: состояние без блокировки, состояние смещенной блокировки, облегченное состояние блокировки и тяжелое состояние блокировки.Они будут постепенно обостряться по мере того, как конкуренция будет становиться все более жесткой. Обратите внимание, что блокировки можно повышать, но нельзя понижать.Эта стратегия предназначена для повышения эффективности получения и снятия блокировок.
2.1.1, спин-блокировка
Блокирование и пробуждение потоков требует переключения ЦП из пользовательского режима в режим ядра.Частые блокировки и пробуждение — это большая нагрузка на ЦП, что неизбежно оказывает большое давление на одновременную производительность системы. В то же время мы обнаружили, что во многих приложениях состояние блокировки объекта будет длиться только в течение короткого периода времени, и не стоит часто блокировать и пробуждать потоки в течение этого короткого периода времени.
Что такое спин-блокировка?
Так называемая спиновая блокировка позволяет потоку ждать некоторое время, а не приостанавливаться немедленно, чтобы посмотреть, не скоро ли поток, удерживающий блокировку, освободит блокировку.
2.1.2 Адаптивная блокировка вращения
В JDK 1.6 появилась более умная спин-блокировка, адаптивная спин-блокировка. Так называемая самоадаптация означает, что количество вращений больше не фиксировано, оно определяется предыдущим временем вращения на том же замке и состоянием владельца замка.
Как это делается?
Если поток завершится успешно, количество повторов в следующий раз будет больше, потому что виртуальная машина считает, что, поскольку последний раз был успешным, этот спин, скорее всего, будет успешным снова, поэтому она позволит ему подождать, чтобы продолжиться еще раз. И наоборот, если для определенной блокировки будет несколько успешных спинов, количество спинов будет уменьшено или даже пропущено, когда блокировка потребуется в будущем, чтобы не тратить ресурсы процессора.Благодаря адаптивной спиновой блокировке, постоянному совершенствованию информации о запуске программ и мониторинге производительности предсказание виртуальной машиной состояния блокировки программы будет становиться все более и более точным, а виртуальная машина будет становиться все более и более интеллектуальной.
2.1.3, устранение блокировки
Чтобы обеспечить целостность данных, нам нужно синхронизировать эту часть операции во время операции, но в некоторых случаях JVM обнаруживает, что нет конкуренции общих данных, а это означает, что JVM устранит эти блокировки синхронизации. Основой для устранения блокировок являются данные, полученные в результате анализа побегов.
Когда мы используем некоторые из встроенных jdk API, такие как StringBuffer, вектор, Hashtable и т. Д., На этот раз будет невидимая операция блокировки.
Например, метод append() для StringBuffer и метод add() для Vector:
public void vectorTest(){
Vector<String> vector = new Vector<String>();
for(int i = 0 ; i < 10 ; i++){
vector.add(i + "");
}
System.out.println(vector);
}
При запуске этого кода JVM, очевидно, может обнаружить, что переменный вектор не выходит за пределы метода vectorTest(), поэтому JVM может смело исключить операцию блокировки внутри вектора.
2.1.4 Огрубление блокировки
Мы знаем, что при использовании синхронизирующих блокировок нам нужно, чтобы область синхронизированного блока была как можно меньше, и синхронизироваться только в фактической области общих данных. Цель этого состоит в том, чтобы свести к минимуму количество операций, которые необходимо синхронизировать.Если есть конкуренция за блокировку, потоки, ожидающие блокировки, могут получить блокировку как можно скорее. В большинстве случаев указанный выше пункт верен.Однако, если серия непрерывных операций блокировки и разблокировки может привести к ненужной потере производительности, вводится концепция укрупнения блокировки.
Так что же такое огрубление блокировки?
Он заключается в соединении нескольких последовательных операций блокировки и разблокировки вместе, чтобы расширить блокировку с более широким диапазоном.
Как в приведенном выше примере: каждый раз, когда добавляется вектор, требуется операция блокировки.JVM обнаруживает, что один и тот же объект (вектор) постоянно блокируется и разблокируется, и будет сочетать более широкий спектр операций блокировки и разблокировки, то есть блокировку и разблокировка.Операция перемещается за пределы цикла for.
2.1.5 Блокировка смещения
Основная цель введения предвзятых блокировок — свести к минимуму ненужные пути выполнения легковесных блокировок без конкуренции многопоточности. Операции блокировки и разблокировки облегченных замков должны полагаться на несколько атомарных инструкций CAS. Так как же предвзятая блокировка уменьшает количество ненужных операций CAS? Мы можем видеть структуру работы Марка, чтобы понять.На самом деле, просто проверьте, хранит ли Mark Word заголовка объекта предвзятую блокировку, указывающую на текущий поток.. Если проверка прошла успешно, поток получил блокировку. Если тест не пройден: вам нужно проверить, установлен ли флаг предвзятой блокировки в 1 (указывая, что в настоящее время это предвзятая блокировка). Если он не установлен, вы можете использовать CAS только для борьбы за блокировку. Если он установлен , попробуйте использовать CAS для смещения заголовка объекта Блокировка указывает на текущий поток.
Процесс выглядит следующим образом:
- Определить, находится ли слово метки в отклоняемом состоянии, то есть является ли оно смещенной блокировкой 1 и флагом блокировки является 01;
- Если он находится в смещенном состоянии, проверьте, является ли идентификатор потока текущим идентификатором потока, если да, выполните шаг (5), в противном случае выполните шаг (3);
- Если идентификатор потока не является идентификатором текущего потока, конкурировать за блокировку с помощью операции CAS, и соревнование прошло успешно, то заменить идентификатор потока Mark Word на текущий идентификатор потока, в противном случае выполнить поток (4);
- Неспособность конкурировать за блокировку через CAS доказывает, что существует ситуация конкуренции с несколькими потоками.Когда достигается глобальная безопасная точка, поток, который получает смещенную блокировку, приостанавливается, смещенная блокировка обновляется до облегченной блокировки, а затем поток, заблокированный в безопасной точке, продолжает выполняться Синхронизированный блок кода;
- Выполнить синхронизированный блок кода.
Снятие блокировки блокировки используется для использования механизма, который только снимает блокировку.Поток не будет брать на себя инициативу снятия блокировки смещения, и вам нужно подождать, пока другие потоки будут конкурировать. Отмена блокировки смещения требует ожидания глобальной точки безопасности (этой точкой времени является код, который не выполняется).
Шаги следующие:
- Приостановите нить, которая владеет смещенным замком, и оцените, заблокирован ли еще камень объекта замка;
- Отменить смещенную блокировку и вернуться в состояние без блокировки (01) или в состояние облегченной блокировки.
2.1.6 Легкий замок
Основная цель введения облегченных блокировок — снизить потребление производительности традиционными тяжеловесными блокировками, использующими мьютексы операционной системы, при условии, что конкурирует лишь небольшое количество потоков.
Когда функция смещенной блокировки отключена или несколько потоков конкурируют за смещенную блокировку, а смещенная блокировка обновляется до облегченной блокировки, предпринимается попытка получить облегченную блокировку. Шаги следующие:
- Определите, находится ли текущий объект в состоянии без блокировки (хэш-код, 0, 01).Если это так, JVM сначала создаст пространство с именем Lock Record в кадре стека текущего потока для хранения текущей метки объекта блокировки. Копия Word (к этой копии официально добавлен префикс Displaced, то есть Displaced Mark Word), в противном случае перейдите к шагу (3);
- JVM использует операцию CAS, чтобы попытаться обновить Mark Word объекта до указателя на Lock Record.Если это успешно означает, что блокировка оспаривается, флаг блокировки будет изменен на 00 (указывая, что объект находится в состоянии блокировки). легкое состояние блокировки), и будет выполнена операция синхронизации; если она не удалась, выполните шаг (3);
- Определить, указывает ли Mark Word текущего объекта на кадр стека текущего потока.Если да, то это означает, что текущий поток уже удерживает блокировку текущего объекта, и блок кода синхронизации выполняется напрямую, в противном случае он может означает только то, что объект блокировки был вытеснен другими потоками.В настоящее время легкий замок необходимо расширить до тяжелого замка., флаг блокировки становится равным 10, и ожидающий поток переходит в состояние блокировки;
Разблокировка замка Разблокировка облегченного замка также осуществляется с помощью операции CAS.Основные этапы заключаются в следующем:
- Выньте данные, сохраненные в перемещенном мечестве. Слово после получения легкого блокировки;
- Используйте операцию CAS, чтобы заменить извлеченные данные в отметку слово текущего объекта. Если это успешно, это означает, что блокировка успешно выделяется, в противном случае выполняется (3);
- Если замена операции CAS не удалась, это означает, что другие потоки пытаются получить блокировку, и приостановленный поток необходимо разбудить при снятии блокировки.
2.1.7, усиленный замок
Тяжеловесная блокировка реализуется монитором внутри объекта. Суть монитора в том, чтобы полагаться на реализацию Mutex Lock базовой операционной системы. Переключение между потоками операционной системы требует переключения из пользовательского режима в режим ядра, а стоимость переключения очень высока.
Откройте эту официальную учетную запись, чтобы поделиться своими знаниями, и продолжим экспортировать их в будущем, надеясь принести помощь читателям и друзьям. Если вы найдете это полезным, пожалуйста, поставьте лайк или добавьте его в закладки, и подпишитесь на общедоступный аккаунт JavaStorm, вы найдете интересную душу.
3. Сравнение замков
Вот сравнение смещенных замков, облегченных замков и тяжелых замков:
Таблица 3 Преимущества и недостатки и применимые сцены
Замок | преимущество | недостаток | Применимая сцена |
---|---|---|---|
Блокировка смещения | Блокировка и разблокировка не требуют дополнительного потребления, а между выполнением асинхронных методов существует всего наносекундный интервал. | Если между потоками существует конкуренция, это приведет к дополнительному потреблению снятия блокировки. | Хорошо, когда только один поток обращается к синхронизированному блоку. |
Легкий замок | Конкурирующие потоки не будут блокироваться, что повышает скорость отклика программы. | Потоки, которые никогда не смогут получить блокировку, используя спин, будут потреблять ресурсы ЦП. | В погоне за временем отклика скорость выполнения синхронизированных блоков очень блочная, и только два потока конкурируют за блокировки. |
тяжелый замок | Конкуренция потоков не использует вращение и не потребляет ЦП | Темы заблокированы, а время отклика медленные | В погоне за пропускной способностью скорость выполнения синхронизированных блоков относительно низкая, а за блокировки конкурируют более двух потоков. |
Если вы найдете это полезным, пожалуйста, поставьте лайк или добавьте его в закладки, и подпишитесь на общедоступный аккаунт JavaStorm, вы найдете интересную душу. Всем отличных выходных!
Использованная литература:
- Чжоу Чжимин: «Глубокое понимание виртуальной машины Java»
- Фан Тэнфэй: «Искусство параллельного программирования на Java»
Обратите внимание на общедоступный номер Javastorm, чтобы получать последний контент