Сводка синхронизированных ключевых слов, которые стоит сохранить

Java задняя часть JVM

Эта статья была добавлена ​​в документ с открытым исходным кодом: JavaGuide (обложка, которая охватывает основные знания, которые необходимо освоить большинству Java-программистов). адрес:GitHub.com/snail Climb/….

Эта статья представляет собой краткий обзор использования ключевого слова synchronized, лежащего в его основе принципа, базовой оптимизации после JDK1.6 и сравнения с ReenTrantLock. Если вы не научились использовать ключевое слово synchronized, его может быть трудно прочитать. Еще две основные статьи, объясняющие ключевое слово synchronized:

Сводка по синхронизированному ключевому слову

Краткое изложение трех основных способов использования ключевого слова synchronized

  • Модифицированный метод экземпляра, который воздействует на текущий экземпляр объекта для блокировки и получает блокировку текущего экземпляра объекта перед вводом кода синхронизации.
  • Измените статический метод, который воздействует на блокировку текущего объекта класса и получает блокировку текущего объекта класса перед вводом кода синхронизации.. То есть блокировка текущего класса будет действовать на все экземпляры объекта класса, потому что статические члены не принадлежат какому-либо экземпляру объекта, а являются членами класса ( static указывает, что это статический ресурс класса, независимо от того, сколько объектов новые, только один экземпляр, поэтому все объекты класса заблокированы). Таким образом, если поток A вызывает нестатический синхронизированный метод объекта экземпляра, а потоку B необходимо вызвать статический синхронизированный метод класса, к которому принадлежит экземпляр объекта, это разрешено, и взаимное исключение не произойдет.Поскольку блокировка, занимаемая при доступе к статическому синхронизированному методу, является блокировкой текущего класса, а блокировка, занимаемая при доступе к нестатическому синхронизированному методу, является блокировкой объекта текущего экземпляра..
  • Измените блок кода, укажите объект блокировки, заблокируйте данный объект и получите блокировку данного объекта перед входом в базу кода синхронизации.Как и метод synchronized, блок synchronized(this) блокирует текущий объект. Синхронизированное ключевое слово добавляется к статическим статическим методам и блокам кода synchronized(class) для блокировки класса Class. Здесь это снова упоминается: ключевое слово synchronized добавляется к нестатическому статическому методу для блокировки экземпляра объекта. Еще одно замечание: старайтесь не использовать synchronized(String a), потому что в JVM пул строковых констант имеет функцию буферизации!

Краткое изложение основного принципа реализации ключевого слова synchronized

  • Реализация блока синхронизированных операторов synchronized использует инструкции monitorenter и monitorexit, где инструкция monitorenter указывает на начало синхронизированного блока кода, а инструкция monitorexit указывает на конец синхронизированного блока кода.Когда выполняется инструкция monitorenter, поток пытается получить блокировку, которая должна получить монитор (объект монитора существует в заголовке объекта каждого объекта Java, и синхронизированная блокировка получает блокировку таким образом, поэтому любой объект в Java можно использовать как блокировку. Причина) имеет право. Когда счетчик равен 0, он может быть успешно получен, а после получения счетчик блокировки устанавливается в 1, то есть увеличивается на 1. Соответственно, после выполнения инструкции monitorexit счетчик блокировки устанавливается в 0, указывая на то, что блокировка снята. Если получение блокировки объекта не удается, текущий поток заблокируется и будет ждать, пока блокировка не будет снята другим потоком.

  • Синхронизированный модифицированный метод не имеет инструкции monitorenter и инструкции monitorexit. Вместо этого, это действительно флаг ACC_SYNCHRONIZED, который указывает, что метод является синхронизированным методом. JVM использует флаг доступа ACC_SYNCHRONIZED, чтобы определить, объявлен ли метод как синхронизированный метод, чтобы выполнить соответствующий синхронный вызов.В более ранних версиях Java синхронизация представляла собой тяжеловесную блокировку и была неэффективной, поскольку блокировки монитора реализовывались с опорой на Mutex Lock базовой операционной системы, а потоки Java отображались на собственные потоки операционной системы. Если вы хотите приостановить или разбудить поток, вам нужна помощь операционной системы, и операционная система должна переключаться из пользовательского режима в режим ядра при переключении между потоками.Переход между этими состояниями занимает относительно много времени, и затраты времени относительно высоки, поэтому ранняя синхронизация неэффективна. К счастью, после Java 6, Java официально оптимизировала синхронизацию с уровня JVM, так что текущая эффективность синхронизированной блокировки также оптимизирована очень хорошо. В JDK1.6 реализовано большое количество оптимизаций реализации блокировок, таких как спин-блокировки, адаптивные спин-блокировки, устранение блокировок, огрубление блокировок, предвзятые блокировки, облегченные блокировки и другие технологии для снижения накладных расходов на операции блокировок.

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

Синхронизированная нижняя сводка по оптимизации ключевых слов

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

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

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

Цель введения предвзятых блокировок аналогична цели введения облегченных блокировок: все они заключаются в том, чтобы снизить потребление производительности традиционными тяжеловесными блокировками с использованием мьютекса операционной системы без многопоточной конкуренции. Но разница в том, что облегченные блокировки используют операции CAS вместо мьютексов без конкуренции. Смещенная блокировка устранит всю синхронизацию при отсутствии конкуренции.

"Смещение" смещенной блокировки - это смещение, которое означает, что она будет смещена в сторону первого потока, который ее получит. Если блокировка не будет получена другими потоками при следующем выполнении, то поток, удерживающий смещенную блокировку быть Синхронизация не требуется! Принцип предвзятой блокировки описан в главе 13, разделе 3 «Оптимизация блокировки» книги «Углубленное понимание виртуальной машины Java: расширенные функции и рекомендации JVM», второе издание.

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

Легкий замок

Если смещенная блокировка не работает, виртуальная машина не будет немедленно обновлена ​​до тяжелой блокировки, она также попытается использовать оптимизацию, называемую облегченной блокировкой (добавлена ​​после версии 1.6).Облегченные блокировки не предназначены для замены тяжелых блокировок. Их первоначальная цель — снизить потребление производительности традиционными тяжелыми блокировками с использованием мьютекса операционной системы без многопоточной конкуренции. Необходимо подать заявку на мьютекс. Кроме того, для запирания и отпирания легких замков используются операции CAS.Для получения информации о принципах блокировки и снятия облегченных блокировок вы можете обратиться к главе 13, разделу 3 «Оптимизация блокировки» второго издания «Углубленное понимание виртуальной машины Java: расширенные функции и рекомендации JVM».

Основой того, что облегченные блокировки могут улучшить производительность синхронизации программ, является то, что «для большинства блокировок нет конкуренции во всем цикле синхронизации», что является эмпирическими данными. Если конфликтов нет, облегченные блокировки используют операции CAS, чтобы избежать накладных расходов, связанных с использованием операций с мьютексами. Но если есть конкуренция за блокировку, в дополнение к накладным расходам мьютекса, будут выполняться дополнительные операции CAS, поэтому при наличии конкуренции за блокировку облегченные блокировки работают медленнее, чем традиционные тяжелые блокировки! Если конкуренция замков жесткая, то легкие замки быстро превратятся в тяжелые замки!

Спин-блокировки и адаптивный спин

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

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

Как правило, потоки не удерживают блокировки слишком долго, поэтому приостанавливать/возобновлять потоки только на это время не стоит.Поэтому команда разработчиков виртуальной машины думает так: "Можем ли мы заставить поток, который запрашивает блокировку, ждать какое-то время без приостановки? Посмотрим, скоро ли поток, удерживающий блокировку, освободит блокировку".Чтобы заставить поток ждать, нам просто нужно заставить поток выполнить цикл занятости (вращение), этот метод называется вращением..

Объяснение энциклопедии Baidu о спин-блокировках:

Что такое спин-блокировка? Он предлагает механизм блокировки для защиты общих ресурсов. На самом деле спин-блокировки аналогичны блокировкам мьютексов, обе из которых предназначены для разрешения взаимоисключающего использования ресурса. Будь то блокировка мьютекса или спин-блокировка, в любой момент времени может быть не более одного держателя, то есть не более одного исполняющего модуля может получить блокировку в любой момент времени. Но у них немного разные механизмы планирования. Для мьютекса, если ресурс уже занят, претендент на ресурс может только войти в состояние сна. Но спин-блокировка не заставляет вызывающего заснуть.Если спин-блокировка уже удерживается другим исполняющим блоком, вызывающая сторона продолжает зацикливаться там, чтобы увидеть, снял ли держатель спин-блокировки блокировку, слово «спин». его имя.

Спин-блокировка была фактически введена до JDK1.6, но по умолчанию она отключена, и ее необходимо пропускать.--XX:+UseSpinningпараметр для включения. После JDK1.6 и 1.6 он был включен по умолчанию. Одно предостережение: ожидание вращения не является полной заменой блокировки, поскольку оно по-прежнему потребляет процессорное время. Если замок занят ненадолго, то конечно эффект очень хороший! Наоборот, наоборот! Время ожидания отжима должно быть ограничено. Если блокировка не получена после вращения более ограниченного числа раз, поток следует приостановить.Значение по умолчанию количества вращений 10 раз, пользователь может изменить--XX:PreBlockSpinизменить.

Кроме того,Адаптивные спин-блокировки были представлены в JDK1.6. Улучшение, приносимое адаптивной блокировкой вращения, заключается в том, что время вращения больше не является фиксированным, а определяется временем вращения на той же блокировке, что и в предыдущий раз, и состоянием владельца блокировки, а виртуальная машина становится все больше и больше. умный".

снятие блокировки

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

блокировка огрубления

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

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

Сводка по ReenTrantLock и синхронизированным ключевым словам

Я рекомендую статью, объясняющую основы ReenTrantLock:«Многопоточное обучение Java (6) использованию блокировок Lock»

Обе блокировки являются реентерабельными.

Обе блокировки являются реентерабельными. Концепция «повторно используемой блокировки» заключается в том, что вы можете снова получить свою собственную внутреннюю блокировку. Например, когда поток получает блокировку объекта, блокировка объекта в это время не снята. Когда он хочет снова получить блокировку этого объекта, он все еще может быть получен. Если он не может быть заблокирован и повторно входить, это вызовет тупик. Каждый раз, когда один и тот же поток получает блокировку, счетчик блокировок увеличивается на 1, поэтому блокировку нельзя снять, пока счетчик блокировок не упадет до 0.

синхронизировано зависит от JVM, а ReenTrantLock зависит от API

Synchronized зависит от реализации JVM.Мы также упоминали ранее, что команда виртуальной машины сделала много оптимизаций для синхронизированного ключевого слова в JDK1.6, но эти оптимизации были реализованы на уровне виртуальной машины и не были представлены нам напрямую. ReenTrantLock реализован на уровне JDK (то есть на уровне API, для которого требуются методы блокировки() и разблокировки с блоками операторов try/finally), поэтому мы можем посмотреть на его исходный код, чтобы увидеть, как он реализован.

ReenTrantLock добавляет некоторые дополнительные функции по сравнению с синхронизированным

По сравнению с синхронизированным, ReenTrantLock добавляет некоторые дополнительные функции. Есть три основных момента:① Ожидание может быть прервано; ② Достигнута справедливая блокировка; ③ Можно добиться выборочного уведомления (блокировки могут быть привязаны к нескольким условиям)

  • ReenTrantLock предоставляет механизм для прерывания потоков, ожидающих блокировки., который реализует этот механизм через lock.lockInterruptably(). Другими словами, ожидающий поток может отказаться от ожидания и вместо этого заняться другими делами.
  • ReenTrantLock может указать, является ли это честной или несправедливой блокировкой. А синхронизироваться может только несправедливая блокировка. Так называемая справедливая блокировка заключается в том, что поток, ожидающий первым, получает блокировку первым.ReenTrantLock по умолчанию является несправедливым, и к нему можно получить доступ через класс ReenTrantLock.ReentrantLock(boolean fair)Конструктор формулирует, справедливо ли это.
  • Ключевое слово synchronized в сочетании с методами wait() и notify/notifyAll() может реализовать механизм ожидания/уведомления.Конечно, класс ReentrantLock также может быть реализован, но он должен полагаться на интерфейс Condition и метод newCondition(). . Условие доступно только после JDK1.5.Он обладает хорошей гибкостью.Например, он может реализовать функцию многоканального уведомления, то есть в объекте блокировки может быть создано несколько экземпляров условия (т.е. объектных мониторов).Объект потока может быть зарегистрирован в указанном условии, так что уведомление о потоке может выполняться выборочно, что обеспечивает большую гибкость при планировании потоков. При использовании метода notify/notifyAll() для уведомления уведомляемый поток выбирается JVM, а класс ReentrantLock в сочетании с экземпляром Condition может обеспечить «выборочное уведомление»., эта функция очень важна и по умолчанию предоставляется интерфейсом Condition. Синхронизированное ключевое слово эквивалентно только одному экземпляру Condition во всем объекте Lock, и все потоки регистрируются в нем. Если метод notifyAll() будет выполнен, все ожидающие потоки будут уведомлены, что вызовет большие проблемы с эффективностью, а метод signalAll() экземпляра Condition только разбудит все ожидающие потоки, зарегистрированные в экземпляре Condition.

Если вы хотите использовать вышеуказанные функции, то выбор ReenTrantLock — хороший выбор.

Производительность больше не является критерием выбора

До JDK1.6 производительность synchronized была намного хуже, чем у ReenTrantLock. Конкретно это выражается так: пропускная способность синхронизированного ключевого слова увеличивается, а количество потоков очень серьезно уменьшается. А ReenTrantLock в основном поддерживает относительно стабильный уровень. Я думаю, это также отражает тот факт, что у ключевого слова synchronized еще много возможностей для оптимизации. Последующее техническое развитие также доказало это.Мы также упоминали выше, что команда JVM сделала много оптимизаций ключевого слова synchronized после JDK1.6.После JDK1.6 производительность synchronized и ReenTrantLock практически одинакова. Таким образом, все статьи в Интернете, в которых говорится, что ReenTrantLock выбран из-за производительности, неверны! После JDK1.6 производительность больше не является фактором, влияющим на выбор синхронизированных и ReenTrantLock! Кроме того, виртуальная машина будет более склонна к собственной синхронизации в будущем для повышения производительности, поэтому рекомендуется сначала использовать синхронизированное ключевое слово для синхронизации, если синхронизированное может удовлетворить ваши потребности! Оптимизированный синхронизированный, как и ReenTrantLock, во многих местах использует CAS-операции..

Ссылаться на

Если ты расцветешь, ветерок придет. Добро пожаловать в мою общедоступную учетную запись WeChat: «Руководство по прохождению интервью на Java», теплую общедоступную учетную запись WeChat. Ответьте на ключевое слово "1" в фоновом режиме официального аккаунта, возможно, вы увидите то, что хотите!