Резюме
Принцип синхронизированной блокировки также является вопросом, который часто возникает в интервью с крупными фабриками.Эта статья в основном анализирует и объясняет следующие вопросы, чтобы помочь вам понять принцип синхронизированной блокировки.
1. Что такое синхронизированная блокировка? Что является объектом блокировки?
2. Каков процесс выполнения блокировки смещения, облегченной блокировки и тяжелой блокировки?
3. Почему несправедливо говорить о легких и тяжелых замках?
4. Зачем тяжеловесным замкам нужны операции вращения?
5. Когда произойдет эскалация блокировки и понижение уровня блокировки?
6. Каковы применимые сценарии блокировки смещения, облегченной блокировки и блокировки веса, каковы их преимущества и недостатки?
1. Что такое синхронизированная блокировка? Что является объектом блокировки?
Английское значение synchronized — это значение синхронизации, то есть методы и блоки кода, которые могут быть изменены с помощью synchronized, могут выполняться только одним потоком за раз, чтобы обеспечить безопасность данных.
Как правило, блоки синхронного кода, методы экземпляров и статические методы можно изменять, а заблокированными объектами являются объекты в квадратных скобках блока синхронного кода, объектов экземпляров и классов.
С точки зрения реализации,
- synchronized изменяет блок кода синхронизации.Когда javac компилируется, соответствующие инструкции monitorenter и monitorexit генерируются, соответственно, до инструкции входа и после инструкции выхода синхронизированного блока синхронизации, представляя попытку получить и снять блокировку. (Чтобы гарантировать, что блокировку можно снять даже в случае возникновения исключения, javac добавляет неявную попытку try-finally в блок синхронизированного кода, в котором вызывается команда monitorexit для снятия блокировки.)
- Метод синхронизированной модификации, javac добавляет ключевое слово ACC_SYNCHRONIZED к атрибуту flags метода.Когда JVM вызывает метод и обнаруживает, что вызываемый метод изменен ACC_SYNCHRONIZED, она сначала пытается получить блокировку.
public class SyncTest {
private Object lockObject = new Object();
public void syncBlock(){
//修饰代码块,加锁对象为lockObject
synchronized (lockObject){
System.out.println("hello block");
}
}
//修饰实例方法,加锁对象为当前的实例对象
public synchronized void syncMethod(){
System.out.println("hello method");
}
//修饰静态方法,加锁对象为当前的类
public static synchronized void staticSyncMethod(){
System.out.println("hello method");
}
}
2. Каков процесс выполнения блокировки смещения, облегченной блокировки и тяжелой блокировки?
В JVM объект Java фактически состоит иззаголовок объекта+данные экземпляра+Выровнять отступыОн состоит из трех частей, а заголовок объекта в основном содержитMark Word+ Указатель на класс, к которому принадлежит объект (и длину, если это объект массива). Как на изображении ниже:
Mark Word: Хранит данные времени выполнения самого объекта, такие как hashCode, возраст генерации GC, флаги состояния блокировки, блокировки, удерживаемые потоками, и т. д. Он занимает 4 байта в 32-битной системе и 8 байтов в 64-битной системе, поэтому объем данных, которые он может хранить, ограничен, поэтому в основном это достигается за счет настройкиСмещать ли бит флага замкаизамок флагКакие данные используются для различения других сохраненных битов, см. следующий рисунок:
Информация о блокировке хранится в объекте блокировки.Mark Word, когда состояние объекта является предвзятой блокировкой,Mark WordСохраненный — это смещенный идентификатор потока; когда состояние представляет собой облегченную блокировку,Mark WordСохраняет указатель на стек потокаLock Recordуказатель; когда состояние представляет собой тяжеловесную блокировку,Mark Wordуказатель на объект монитора в куче.
Это блок-схема, найденная в Интернете. Вы можете сначала посмотреть на блок-схему и скомбинировать текст, чтобы понять процесс выполнения.
Блокировка смещения
Автор Hotspot после предыдущих исследований обнаружил, что в большинстве случаевЗамки не только бесплатно из многопоточных соревнований, но всегда приобретаются несколько раз по той же нити, поэтому вводится смещенная блокировка.
Проще говоря, когда основная блокировка находится в состоянии блокировки смещения, идентификатор потока, который в настоящее время удерживает блокировку смещения, будет сохранен в слове маркировки.Если идентификатор потока, который получает блокировку, согласуется с ним, это означает, что он один и тот же поток, который может выполняться напрямую без необходимости.Выполнять операции CAS, такие как облегченная блокировка для блокировки и разблокировки.
Процесс блокировки смещенного замка:
Сценарий 1: Когда объект блокировки впервые получен потоком
Если обнаруживается, что поток находится в анонимном смещенном состоянии (т. е. слово метки объекта блокировки не хранит идентификатор потока), он будет использовать инструкцию CAS дляmark wordИдентификатор потока изменяется с 0 на текущий идентификатор потока. Если это удается, это означает, что смещенная блокировка получена, и код в синхронизированном блоке продолжает выполняться. В противном случае смещенная блокировка будет аннулирована и преобразована в облегченную блокировку.
Сценарий 2. Когда поток, получивший смещенную блокировку, снова входит в синхронизированный блок.
Обнаружено, что идентификатор потока, сохраненный объектом блокировки, является идентификатором текущего потока, и запись будет добавлена в стек текущего потока.Displaced Mark WordпустойLock Record, а затем продолжить выполнение кода синхронизированного блока, поскольку манипулируется стеком, принадлежащим потоку, поэтому нет необходимости использовать инструкцию CAS; видно, что в режиме смещенной блокировки, когда смещенный поток пытается снова получить блокировку, только несколько. В этом случае будет достаточно простой операции,synchronizedНакладные расходы на производительность ключевых слов можно игнорировать.
Сценарий 2: когда поток, который не получает блокировку, входит в синхронизированный блок
Когда поток, который не получил блокировку, входит в синхронизированный блок и обнаруживает, что в настоящее время он находится в состоянии блокировки со смещением и хранит идентификаторы других потоков (то есть другие потоки удерживают смещенные блокировки), он войдет в синхронизированный блок.Отменить блокировку смещенияЛогика , вообще говоря, будет вsafepointчтобы увидеть, жив ли еще предвзятый поток
- Если поток жив и все еще выполняется в синхронизированном блоке, Затем обновите блокировку до облегченной блокировки, и исходный смещенный поток продолжит владеть блокировкой, но удержит облегченную блокировку и продолжит выполнение блока кода.Поток вращается, пытаясь получить облегченную блокировку.
- Если смещенный поток умер или не находится в синхронизированном блоке,
заголовок объекта
mark wordИзменить на разблокированное состояние (разблокировано)
Отсюда видно, что смещениеблокировка обновленияВремя:Когда поток получает смещенную блокировку во время выполнения, пока другой поток пытается получить смещенную блокировку, а поток, в настоящее время удерживающий смещенную блокировку, все еще выполняется в синхронизированном блоке, смещенная блокировка будет обновлена до облегченной блокировки.
Процесс разблокировки блокировки смещения
Таким образом, разблокировка смещенной блокировки очень проста: удаляется только последняя запись в стеке потока.lock recordизobjПоле имеет значение null. Следует отметить, что на этапе разблокировки замка смещенияОн не изменяет идентификатор потока в объекте блокировки Mark Word.Проще говоря, когда объект блокировки находится в предвзятой блокировке, идентификатор потока в Mark Word может быть идентификатором потока, выполняющего блок синхронизации, или он может быть освобожден после последнего выполнения., главным образом потому, что поток, который удерживал блокировку смещения в прошлый раз, выполняет блок синхронизации в следующий раз, и его можно выполнить напрямую, определив, что идентификатор потока в слове метки тот же, вместо того, чтобы устанавливать свой собственный идентификатор потока для объекта блокировки. Отметьте слово через середину операции CAS.Это примерный поток выполнения предвзятой блокировки:
Легкий замок
Тяжеловесная блокировка реализуется с помощью Mutex Lock базовой операционной системы, но поскольку использование Mutex Lock требует приостановки текущего потока и переключения из пользовательского режима в режим ядра для выполнения, стоимость этого переключения очень высока. , и в большинстве случаев многопоточная конкуренция может отсутствовать, но в течение этого периода поток A выполняет синхронизированный блок, а другой период времени — поток B для выполнения синхронизированного блока. выполняются попеременно, а не одновременно, и конкуренции нет Тяжеловесные замки менее эффективны. А в тяжеловесной блокировке поток, не получивший блокировку, будет заблокирован, а поток после получения блокировки будет пробужден.Операции блокировки и пробуждения занимают много времени.Если код блока синхронизации выполняется быстро, поток, ожидающий блокировки, может быть выполнен первым.Выполнить операцию вращения (то есть не освобождать ЦП, выполнить несколько пустых инструкций или несколько циклов for) и дождаться получения блокировки, что более эффективно. Таким образом, легкие замки, естественно, нацелены на сценарии, в которых нет конкуренции замков.Если есть конкуренция замков, но не жесткая, спин-блокировки все еще можно использовать для оптимизации, а после неудачи вращения их можно модернизировать до тяжелых замков.
Процесс запирания облегченных замков
JVM создает пространство для каждого потока в кадре стека текущего потока для хранения записей блокировки, которые мы называем Displaced Mark Word. Если поток находит упрощенную блокировку при получении блокировки, он копирует заблокированное слово-метку в свое собственное слово-метку смещения.
Затем поток пытаетсяИспользуйте операцию CAS, чтобы заменить слово метки блокировки указателем записи блокировки, скопированной в собственный стек потоков.. Если это удается, текущий поток получает блокировку, если это не удается, это означает, что слово метки было заменено записью блокировки других потоков, что указывает на то, что текущий поток пытается использовать запись блокировки, конкурируя с другими потоками.вращениеполучить замок.
Spin: продолжайте попытки получить блокировку, обычно реализованную в цикле.
Вращение должно потреблять ЦП. Если блокировка не может быть получена все время, поток будет все время находиться в состоянии вращения, напрасно тратя ресурсы ЦП.
JDK использует адаптивное вращение, Короче говоря, если поток завершится успешно, количество вращений в следующий раз будет больше, а если вращение завершится неудачно, количество вращений будет уменьшено.
Спин не продолжается все время.Если спин достигает определенного уровня (связанного с JVM и операционной системой), блокировка все еще не получена, что называется сбоем спина, тогда поток будет заблокирован. В то же время замок будет модернизирован до тяжелого замка.
Облегченный процесс открытия замка
При освобождении блокировки текущий поток будет использовать операцию CAS для копирования содержимого перемещенного слова-метки обратно в заблокированное слово-метку. Если конфликтов нет, операция копирования завершается успешно. Если есть другие потоки, которые повторяются несколько раз и вызывают обновление облегченной блокировки до тяжелой блокировки, операция CAS завершится ошибкой, и блокировка будет снята, а заблокированный поток будет разбужен.Блокировка и разблокировка блок-схемы легкого замка:
тяжелый замок
Когда несколько потоков одновременно запрашивают тяжеловесную блокировку, тяжеловесная блокировка устанавливает несколько состояний, чтобы различать запрашивающие потоки:
Contention List: Все потоки, запрашивающие блокировки, будут помещены в конкурсную очередь первыми. Не знаю, почему в статьях в Интернете это называется очередью. На самом деле эта очередь - first in, last out, больше похожа на стек, т.е. когда Entry List пуст, поток Owner напрямую возьмет поток из хвоста очереди Contention List (из потока, добавленного позже) и сделает его потоком OnDeck для борьбы за блокировку. (Основная причина в том, что поток, который только что пришел для получения тяжеловесной блокировки, должен выполнить операцию вращения для получения блокировки, и он войдет в список конфликтов, если он не будет получен, поэтому поток OnDeck в основном все еще вращается и имеет не внесен в список состязаний. конфликт в потоке)
Entry List: потоки в списке состязания, которые могут быть кандидатами, перемещаются в список входа, в основном для уменьшения одновременного доступа к списку состязания, поскольку новые потоки добавляются в конец очереди, а потоки берутся из конца очереди. .
Wait Set: потоки, заблокированные вызовом метода ожидания, помещаются в набор ожидания.
OnDeck: Максимум один поток конкурирует за блокировку в любое время, и этот поток называется OnDeck.
Owner: Поток, получивший блокировку, называется владельцем. .!Owner: поток, освобождающий блокировку
Процесс выполнения тяжеловесной блокировки:
Блок-схема выглядит следующим образом:
Шаг 1 заключается в том, что до того, как поток заблокируется и будет ждать при входе в список конфликтов, процесс сначала попытается прокрутить и использовать операцию CAS для получения блокировки, и если она не может быть получена, он войдет в конец очереди списка конфликтов. .
Шаг 2: Когда поток владельца разблокирован, если список записей пуст, он сначала переместит некоторые потоки в конце очереди в списке конфликтов в список записей.
Шаг 3: Когда поток Owner разблокируется, если Entry List не пуст, возьмите поток из Entry List и сделайте его потоком OnDeck.Поток Owner не передает блокировку напрямую потоку OnDeck, но передает право для блокировки конкуренции.Для OnDeck, OnDeck должен повторно конкурировать за блокировки, и такое поведение выбора в JVM называется "переключение конкуренции". (главным образом потому, что он еще не вступил в Конфликт Список, конфликт потоков, который все еще вращается для получения тяжелых блокировок)
Шаг 4 заключается в том, что поток OnDeck получает блокировку и становится потоком-владельцем для выполнения.
Шаг 5 заключается в том, что поток владельца вызывает метод ожидания () объекта блокировки для ожидания, он будет перемещен в набор ожидания, и ресурсы ЦП будут освобождены, и блокировка будет снята в то же время.
Шаг 6. Когда другие потоки вызывают метод notify() объекта блокировки, поток, ожидающий вызова метода ожидания, перемещается из набора ожидания в список входов, ожидая получения блокировки.
3. Почему несправедливо говорить о легких и тяжелых замках?
Поскольку предвзятые блокировки не связаны с конкуренцией нескольких потоков, это несправедливо и несправедливо.Используйте операцию CAS, чтобы заменить слово метки блокировки указателем на скопированную запись блокировки в собственном стеке потоков., так что кто сможет получить замок, зависит от удачи, независимо от порядка. Несправедливость тяжеловесных блокировок заключается главным образом в том, что поток, который только что вошел в тяжеловесную блокировку, не попадет напрямую в очередь списка конфликтов, а будет вращаться, чтобы получить блокировку, поэтому поток, который приходит позже, имеет определенный шанс получить блокировку первым. , так что это несправедливо.
4. Зачем тяжеловесным замкам нужны операции вращения?
Поскольку все эти потоки в ContetionList, EntryList и WaitSet заблокированы, операция блокировки выполняется операционной системой (через функцию pthread_mutex_lock в Linxu). После того, как поток заблокирован, он переходит в состояние планирования ядра (Linux), что заставит систему переключаться между пользовательским режимом и режимом ядра, что серьезно влияет на производительность блокировки. Если код в блоке синхронизации относительно мал, а выполнение выполняется относительно быстро, поток, который приходит позже, сначала получает блокировку, выполняет ее первым и не входит в состояние блокировки, что снижает дополнительные накладные расходы и может улучшить пропускная способность системы.
5. Когда произойдет эскалация блокировки и понижение уровня блокировки?
Смещенный замок модернизирован до облегченного замка:Это когда разные потоки конкурируют за блокировки. В частности, когда поток обнаруживает, что текущее состояние блокировки является предвзятой блокировкой, идентификатор потока, хранящийся в объекте блокировки, является идентификатором других потоков, а поле obj записи блокировки, запрошенной из стека потока, соответствующего идентификатору потока, равно не нуль (представляющий текущий поток, удерживающий смещенную блокировку, все еще выполняет синхронизированный блок). Затем смещенный замок будет модернизирован до облегченного замка.
Легкие замки модернизируются до тяжелых замков:Даже в легковесной блокировке поток, который не получил блокировку, будет вращаться, и блокировка будет обновлена, если блокировка не была получена после определенного количества циклов, потому что цикл также занимает ЦП, а долгосрочная спина также очень требовательна к производительности.заблокировать понижение версииПотому что, если нет многопоточной конкуренции, использование тяжеловесных блокировок вызовет дополнительные накладные расходы, поэтому, когда JVM входит в безопасную точку SafePoint (можно просто считать, что безопасная точка заключается в том, что все пользовательские потоки останавливаются, и только может выполняться поток сборки мусора JVM) когда проверяет наличие бездействующих мониторов, а затем пытается перейти на более раннюю версию.
6. Каковы применимые сценарии блокировки смещения, облегченной блокировки и блокировки веса, каковы их преимущества и недостатки?
Пространство ограничено, вот плюсы и минусы различных блокировок из The Art of Concurrent Programming:
| Замок | преимущество | недостаток | Применимая сцена |
|---|---|---|---|
| Блокировка смещения | Блокировка и разблокировка не требуют дополнительного потребления, а разрыв составляет всего наносекунду по сравнению с выполнением асинхронных методов. | Если между потоками существует конкуренция за блокировку, это приведет к дополнительному потреблению отзыва блокировки. | Подходит для сценариев, когда к синхронизированному блоку обращается только один поток. |
| Легкий замок | Конкурирующие потоки не будут блокироваться, что повышает скорость отклика программы. | Использование spin будет потреблять ЦП, если потоки, которые никогда не получают блокировку. | Соблюдайте время отклика. Синхронизированные блоки выполняются очень быстро. |
| тяжелый замок | Конкуренция потоков не использует вращение и не потребляет ресурсы ЦП. | Поток заблокирован, и время ответа медленное. | Следите за пропускной способностью. Синхронизированные блоки выполняются дольше. |
Ссылка на ссылку:
красный паук.группа:4000/статья/02/…