«Эта статья участвовала в мероприятии Haowen Convocation Order, щелкните, чтобы просмотреть:Двойные заявки на внутреннюю и внешнюю стороны, призовой фонд в 20 000 юаней ждет вас, чтобы бросить вызов!"
В предыдущей статье мы потратили много времени на анализ и понимание
volatile
Ключевые слова, заинтересованные студенты могут выйти и повернуть налево«Здравствуйте, расскажите, пожалуйста, о ключевом слове volatile? (полная статья)". законченныйvolatile
, конечно, без разговоровsynchronized
, в параллельном программировании это ключевое слово всегда существовало на старшем уровне, мы привыкли называть еготяжелый замок, но сJava SE 1.6
После версии,Java
командная параsynchronized
Он был тщательно оптимизирован, так что он может быть легким или тяжелым, соленым или сладким (ерунда). Следуйте моему видению, и давайте посмотрим, что это такое!
1. Душевная пытка
Прежде чем перейти к сути, это все еще клишированная пытка души, вы готовы?
- что
synchronized
, как он используется? -
synchronized
Два способа реализовать блокировку объекта и как это работает? - говорить о
synchronized
Заблокировать процесс эскалации? -
synchronized
иvolatile
разница?
Я не знаю, сложно ли это для вас, позвольте мне провести вас, чтобы проанализировать эту технологию от более мелкого к более глубокому.
2. Базовое приложение
2.1 Метод блокировки
synchronized
Существует три метода блокировки, включая модифицированные статические методы, модифицированные методы экземпляра и модифицированные блоки кода.Различные методы блокировки влияют на степень детализации управления блокировками и должны определяться в соответствии с фактической инженерной средой.
- Статический метод действует на текущий объект класса, то есть гранулярность блокировки - это сам объект класса.Для входа в статический метод необходимо получить блокировку объекта класса.
- Метод экземпляра действует на экземпляр текущего класса, то есть гранулярность блокировки — это экземпляр класса, и для входа в метод экземпляра необходимо получить блокировку экземпляра класса.
- Блок кода воздействует на указанный объект блокировки, то есть гранулярность блокировки представляет собой указанный объект блокировки, и для входа в блок кода необходимо получить блокировку указанного объекта.
2.2 Применение кода
public class SyncExample {
private static final Object LOCK = new Object();
private static int i = 1;
public synchronized static void increase1(){
i++;
}
public synchronized void increase2(){
i++;
}
public void increase3(){
synchronized (LOCK){
i++;
}
}
}
#increase1
Метод представляет собой модифицированный статический метод;#increase2
Метод представляет модифицированный метод экземпляра;#increase1
Метод представляет собой оформленный блок кода.
3. Блокировка схемы хранения
synchronized
Всегда связан с объектом. Если метод статический, ассоциированным объектом является класс; если метод нестатический, ассоциированным объектом является экземпляр. Если это кодовый блок, то указанный объект. Очевидно, блокировка записывается в объект. Итак, возникает вопрос,synchronized
К чему относится замок? В простом понимании замок — это общий ресурс, который записывает, кто им владеет, каково текущее состояние и т. д. Давайте сначала проанализируем, как объекты хранятся в памяти.
существуетHotspot JVM
середина,Java Object
Схема хранения объектов в памяти разделена на три области, а именно: заголовок объекта, демонстрационные данные и заполнение объекта. Как показано на рисунке, схема хранения массивов и объектов очень похожа, за исключением того, что заголовок объекта больше, чем длина массива, потому что массив должен хранить свою собственную длину, которая составляет 4 байта.
Как видно из рисунка, заголовок объекта включает в себя две части, а именно тег объекта и метаинформацию класса (указатель типа). тег объекта, т.е.Markword
объект храненияhashCode
, GC
Информация и замки. Метаинформация класса хранит «указатели на информацию об объекте класса». в 32 битJVM
, заголовок объекта занимает 8 байт, а 64-битныйJVM
Занимает 16 байт.
Как показано выше, этоMarkword
класс в 32-битнойJVM
план хранения для различных ситуаций,Markword
Хранящиеся в нем данные будут изменяться при изменении бита флага блокировки, и примерно сохраненные изменения делятся на пять ситуаций. На рисунке мы можем увидеть процесс перехода от блокировки без блокировки -> смещенная блокировка -> облегченная блокировка -> хранилище тяжеловесных блокировок, что представляет собой процесс укрупнения блокировок.
Итак, вопрос в том, могут ли все объекты реализовывать блокировки? Ответ положительный.
- Сначала у нас есть для
Java
Существует общее понимание того, что все объекты являются производными отObject
, каждый объект хранится в памяти, как показано на нашем рисунке, есть заголовок объекта, а заголовок объекта имеетMarkword
Тег объекта. Важно отметить, что хранилище объектов включает в себяMarkword
Реализация маркировки объектовnative
Да, обаC++
Объекты, реализованные языком. - Когда поток получает блокировку, он фактически получает объект монитора, который является объектом синхронизации.
Java Object
содержит этот объект. Аналогично, этот объект такжеnative
из.
В-четвертых, обновление блокировки
Java 1.6
До,synchronized
Это стандартная тяжеловесная блокировка. Когда несколько потоков конкурируют за общие ресурсы, потоки, которые не конкурировали за ресурсы, всегда будут заблокированы, а производительность высока. В то же время для тяжеловесных блокировок блокировка и освобождение блокировок также потребляют много ресурсов. Чтобы уменьшить накладные расходы на производительность и повысить эффективность, люди разделили четыре состояния блокировки для различных сценариев блокировки, включая отсутствие блокировки, предвзятую блокировку, облегченную блокировку и усиленную блокировку.Степень непрерывно повышается от низкого до высокого.
4.1 Блокировка смещения
Во многих случаях блокировки всегда запрашиваются одним и тем же потоком несколько раз, и потоки, конкурирующие за блокировки, отсутствуют. Для такой ситуации очень подходит смещенный замок, так когда же смещенный замок? В главе 3 мы перечисляемsynchronized
В разных состояниях блокировки,Markword
Существует большая разница в структуре памяти.
4.1.1 Получение предвзятой блокировки
когда поток идет в гостиsynchronized
Когда используется блок кода или метод, украшенный ключевыми словами,Markword
Сохраните идентификатор текущего потока вCAS
сравнить текущийMarkword
Является ли сохраненный идентификатор потока идентификатором потока, пытающегося войти в блок синхронизации, если он равен, нет необходимости снова получать блокировку, и блок кода синхронизации может выполняться напрямую; если он не равен, это означает, что текущая смещенная блокировка смещена в сторону других потоков, и смещенную блокировку необходимо отозвать, а затем обновить блокировку до облегченной блокировки.
4.1.2 Аннулирование блокировки смещения
Отзыв предвзятой блокировки на самом деле не отменяет блокировку и не переводит ее в свободное от блокировки состояние. Для отзыва смещенных замков есть два случая для исходной удерживающей нити и самого замка.
- Если первоначальный удерживающий поток только что завершил выполнение и вышел из блока кода синхронизации, то на этот раз
Markword
Идентификатор сохраненного потока имеет значение null. - Если исходный удерживающий поток все еще выполняется в блоке синхронизированного кода, смещенная блокировка в это время будет обновлена до облегченной блокировки, после чего исходный поток продолжит выполнение.
На рисунке ниже показаноsynchronized
В модифицированном блоке кода синхронизации процесс потока T1 и потока T2 последовательно конкурирует за ресурсы блокировки.
4.2 Легкий замок
В предыдущем разделе упоминалось, что два потока конкурируют за блокировки, что приводит к отзыву предвзятых блокировок.В процессе отзыва происходит обычное расширение блокировки, то есть обновление до облегченных блокировок. Облегченные блокировки подходят для сценариев, в которых два потока конкурируют за ресурсы блокировки, а синхронизированные блоки кода выполняются быстро. что в объектеMarkword
Что изменилось в схеме хранения?
4.2.1 Облегченный процесс обновления
Как мы все знаем, вJVM
, стек является частным для потока. Первый шаг в обновлении до легковесного замка — сделать что-то в кадре стека.
- Кадр стека вновь созданной записи блокировки
LockRecord
, запись включаетdisplaced hdr
иowner
. - заблокирует объект в заголовке
Markword
Содержимое копируется в только что созданный кадр стека.LockRecord
. - записать замок
LockRecord
где владелец указывает на объект блокировки. - Наконец, заголовок объекта
Markword
Указатель на запись блокировки в стеке указывает на запись блокировкиLockRecord
(Этот шаг являетсяMarkword
сохранить реальное изменение содержания).
Процесс изменения показан на рисунке ниже.
4.2.2 Облегченный соревновательный процесс
Когда поток удерживает облегченную блокировку, когда другой поток конкурирует, поток будет ждать в пустом цикле на месте, вместо того, чтобы изменить состояние потока наBlocked
блокирующее состояние. Когда владеющий поток покидает блок синхронизации и освобождает блокировку, другой поток быстро получает блокировку.
Так почему же поток, не завладевший ресурсом блокировки, ожидает циклически, а не блокируется?Наиболее важной причиной этого является необходимость блокировки и пробуждения потока.CPU
От пользовательского режима до режима ядра частые блокировки и пробуждения являются тяжелым бременем для ЦП, что неизбежно оказывает большое влияние на одновременную производительность операционной системы. Так что возьмите петлю, чтобы подождать, этоблокировка спина, СюдаAQS
Также используется нижний слой замка.
Так как же поток, который не получил ресурс блокировки, ожидает циклически?Постоянный цикл потребляетCPU
Производительность, конечно же, у данной спиновой блокировки есть условие остановки, которое делится на два случая.
- существует
Java 1.6
Ранее было установлено количество вращений, и цикл будет прекращен, если количество циклов будет превышено.Как правило, количество раз установлено 10, что можно установить, установивHotSpot 参数 -XX:PreBlockSpin
Чтобы изменить, перед изменением этого параметра вам необходимо сначала установить параметр-XX:+UseSpining
Включите блокировку вращения. - существует
Java 1.6
Позже, по сравнению с интеллектуальной адаптивной блокировкой вращения, этот метод определяет время самовыбора блокировки в соответствии с предыдущим временем и состоянием самовыбора той же блокировки, а не фиксированным количеством вращений.
4.2.3 Снятие блокировки Spinlock
Когда поток, который не получил ресурс блокировки, не может получить блокировку путем вращения, блокировка будет обновлена до тяжеловесной блокировки, а заголовок объекта блокировки будет изменен.Markword
Значение в измененном содержимом примерно соответствует указателю на тяжеловесную блокировку, а флаг модифицированной блокировки равен 10. В этот момент поток блокируется.
Когда синхронизированный блок кода, которому принадлежит блокировка, выходит, он передается черезCAS
Стек хранимых записейMarkword
содержимое и текущий объект блокировкиMarkword
сравнить затем установить, потому что текущийMarkword
Контент изменился, и ему точно не удастся установить значение.В это время поток снимет блокировку, освободит монитор (монитор) и разбудит ожидающий поток. Затем пробуждается другой заблокированный поток, чтобы повторно конкурировать за ресурс блокировки.
4.3 Тяжелый замок
В предыдущем разделе упоминалось, что два потока конкурируют за ресурсы блокировки. Поток, который не получил ресурс блокировки, не получает ресурс блокировки в рамках политики спина, и облегченная блокировка будет обновлена до тяжелой блокировки. реальная блокировка., это мьютекс, блокирующие и разблокирующие ресурсы очень ресурсоемкие. Так где же этот замок и как он выглядит?
4.3.1 Где находится замок
Когда блокировка модернизируется до тяжеловесной блокировки, наиболее очевидным изменением является объект блокировки.Markword
Токен блокировки становится10, указанное содержимое становится указателем на объект монитораMonitor. Как этот объект монитора реализует блокировку мьютекса? Давайте напишем кусок кода, чтобы проверить это.
public class SyncExample {
private static int i = 1;
public synchronized static void increase1() {
i++;
}
public static void main(String[] args) {
synchronized (SyncExample.class){
}
SyncExample.increase1();
}
}
Мы сначалаjava
файл, скомпилированный вSyncExample.class
, то черезjavap -v SyncExample.class
Инструкция декомпилирует указанныйJava
файл с байт-кодом.
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #3 // class me/stone/training/platform/training/java/thread/SyncExample
2: dup
3: astore_1
4: monitorenter
5: aload_1
6: monitorexit
7: goto 15
10: astore_2
11: aload_1
12: monitorexit
13: aload_2
14: athrow
15: invokestatic #4 // Method increase1:()V
18: return
Мы можем ясно видеть, что в блоке синхронизированного кода больше байт-кода.monitorenter
иmonitorexit
, эти две инструкции очень важны.
-
monitorenter
Представляет для получения объекта монитораmonitor
Приобретение других конкурирующих потоков может войти только после успеха очереди ожидания, она заблокирована. -
monitorexit
Представляет объект монитора выпускаmonitor
права собственности, чтобы другие заблокированные потоки могли попытаться получить объект монитора.
4.3.2 Объекты мониторинга
synchronized
Модифицированный блок синхронизации, когда несколько потоков конкурируют за ресурсы, конкуренция фактически является объектом блокировки, и этот объект блокировки являетсяObject
Объект, который содержит объект в памяти, заголовок объекта имеет указатель наMarkword
, когда замок модернизируется до усиленного замка,Markword
указывает наMonitor
объект, этот объект на самом деле родной, вJVM Hotspot
, этот объект по существу являетсяC++
объект, представляет собойObjectMonitor
экземпляр класса.
Как показано на фиг.Monitor
Есть четыре важных свойства.
-
_count
,прилавок. Используется для подсчета количества попыток блокировки, мы часто говоримsynchronized
это реентерабельная блокировка, которая является ключом к реализации. -
_owner
, запишите нить текущего держателя замка, то есть того, кто в данный момент держит замок. -
_waitSet
, очередь ожидания, как следует из названия, когда поток вызывает метод ожидания объекта, поток освобождает ресурсы и входит в очередь, чтобы дождаться пробуждения. -
_EntrySet
, очередь входа, в этой очереди ставятся в очередь потоки, которые не могут конкурировать за блокировки, и эти потоки находятся в состоянии блокировки.
Нить приобрела замок в соревновании тяжеловесных замков, в это время_owner
указывает на текущий поток,_count
Счетчик увеличивается на 1, блокировка не получена потоком вentry
Очередь в очереди. Если поток, получивший ресурс блокировки, снова входит в блок синхронизации, управляемый тем же ресурсом блокировки, поскольку суждение_owner
указывая на себя, поэтому_count
Добавьте 1, чтобы реализовать блокировку с повторным входом. Наконец, выйдите из синхронизированных блоков по очереди, постепенно уменьшите счетчик на 1 и, наконец, уменьшите его до 0, выйдите из всех синхронизированных блоков, освободите ресурсы блокировки и уведомитеentry
Очередь может упорядочивать потоки в очереди, чтобы они конкурировали за ресурсы блокировки.
wait
Очередь очень особенная, поток выполняется в процессе удержания блокировки#wait
метод, поток освободит объект блокировки и войдет в эту очередь. все жду#notify
Метод просыпается, а затем входит в очередь ожидания, чтобы конкурировать с другими потоками за ресурсы блокировки.
должны знать о том,#notifyМетод заключается в том, чтобы взять случайный
wait
Потоки в очереди помещаются вentry
очередь,#notifyAll
заключается в том, чтобы поместить все ожидающие потоки вentry
очередь .
4.3.3 Нечестная блокировка
synchronized
Тяжеловесная блокировка не является справедливой блокировкой, то есть когда поток, удерживающий ресурс, освобождает ресурс блокировки, он помещается вentry
Потоки очереди будут синхронизироваться и конкурировать за ресурсы блокировки и не позволят потоку с наибольшим временем ожидания напрямую получить блокировку, и даже возможно, что, когда новый поток внезапно попытается получить блокировку, поток, удерживающий ресурс блокировки, просто освобождает блокировку.Поток получит ресурс блокировки. Недостатком несправедливых блокировок является то, что некоторые потоки могут не иметь возможности получить ресурсы блокировки в течение длительного времени и находятся в состоянии голодания.Если вы хотите использовать справедливые блокировки, вы можете использоватьReentrantLock
честный режим блокировки.
Пять, сравните volatile
предыдущая статьяНе паникуйте при встрече | Здравствуйте, можете рассказать о ключевом слове volatile? (полная статья)подробныйvolatile
Использование ключевых слов, принцип. В этой статье мы обобщаемsynchronized
иvolatile
главное отличие.synchronized
используется для реализации модели многопоточности на основе блокировки; иvolatile
включаютAtomic
является неблокирующим, что означает, что потоки не нуждаются в блокировках для доступа к общим переменным.
synchronized | volatile |
---|---|
работает только для блоков или методов | просто работает с переменными |
Он основан на резьбовой модели замков. | С помощью аппаратного обеспечения, плюс неблокирующий алгоритм, это неблокирующий |
Поскольку блокировка устанавливается и снимается, производительность относительно низкая по сравнению с энергозависимой. | Относительно высокая производительность по сравнению с синхронизированным |
Потому что он основан на замках, он может принести риски параллелизма, такие как тупики и живые | Поскольку он не блокируется, он не подвержен рискам параллелизма, таким как взаимоблокировки, живые блокировки и т. д. |
В целом,volatile
Вы не можете контролировать доступ потока к его объекту, это позволяет нескольким потокам работать с ним, не дожидаясь блокировки объекта, поэтому он работает довольно быстро, он гарантирует, что любые изменения переменных будут сброшены в основную память, однако доступ к основная память считается очень дорогой, поэтому накладные расходы на чтение и запись непосредственно в основную память очень велики.
synchronized
Он гарантирует, что часть кода не может быть доступна нескольким потокам одновременно, устраняет все возможные условия гонки, и, поскольку он основан на блокировках, он очень дорог в использовании и приносит в жертву преимущества многопоточного параллелизма некоторым из них. протяженность - скорость.
Итак, как использовать эти два ключевых слова? В дополнение к различным местам, где используется код, есть еще два принципа.
- Когда переменная будет прочитана несколькими потоками, но записана только одним потоком, используйте
volatile
. - Используется, когда несколько потоков читают и записывают переменные
synchronized
.
6. Резюме
Это конец всей статьи. Можете ли вы ответить на вопросы, упомянутые в начале статьи? Я стараюсь писать максимально подробно, но должны быть какие-то очень низкоуровневые принципы, которые не были освещены.Надеюсь, читатели смогут оставить сообщение в комментариях и обсудить вместе. Следующая статья о многопоточностиAQS
Принципы применения и реализации.
Брат, не паникуй! Не стесняйтесь оставлять лайки, обсуждать и комментировать. Добро пожаловать в колонку интервьюНе паникуйте, когда сталкиваетесь | Параллельное программирование на Java, Не беспокойтесь о повышении зарплаты во время собеседования. Также добро пожаловать, чтобы следовать за мной, я должен быть лучшим человеком.