В программном коде C мы можем использовать блокировку MUTEX, предоставленную операционной системой для достижения доступа MUTEX к синхронным блокам, блокировке потоков и работы по пробуждению. Однако в Java, в дополнение к предоставлению Lockapi, на уровне синтаксиса также предоставляются синхронизированные ключевые слова для реализации примитивов синхронизации Mutex. Итак, как вы реализуете синхронизированный ключ в JVM?
1. Синхронизированное представление байт -кодов:
В языке Java есть два встроенных синхронизированных синтаксиса: 1. Синхронизированные утверждения; 2. Синхронизированный метод. Для синхронизированных операторов, когда исходный код Java составлен в байт -код с помощью Javac, будут вставлены инструкции по байт -коду Monitorenter и Monitorexit соответственно в положениях входа и выхода блока синхронизации. Синхронизированный метод будет переведен в обычные инструкции по вызову и возврату метода, такие как: Invokevirtual и Areturn На уровне виртуальной коды нет особой инструкции для реализации синхронизированного модифицированного метода. Вместо этого, синхронизированное положение флага 1 в поле Method Access_flags метода помещается в таблицу методов файла класса, указывая, что метод является синхронизированным методом и использует объект, который вызывает метод, или класс, принадлежащий методу для представления klass как объект блокировки.
2. Оптимизация замков в JVM:
Проще говоря, Monitorenter и Bytecode MonitorEnter и Monitorexit в JVM полагаются на мутекскулон базовой операционной системы для ее реализации. Однако, поскольку использование Mutexlock требует приостановки текущего потока и переключения от состояния пользователя на состояние ядра для выполнения, это переключение очень дорого; Однако в большинстве случаев в реальности метод синхронизации запускается в однопользованной среде (беззамажная конкурентная среда). Если каждый раз вызывается Mutexlock, это серьезно повлияет на производительность программы. Тем не менее, в JDK1.6 было введено много оптимизаций в реализацию замков, таких как грубое покрытие блокировки, устранение блокировки, легкая блокировка, смещенная блокировка, адаптивное спиннинг и другие технологии, чтобы уменьшить накладные расходы на операцию блокировки.
LockCoarsening: то есть уменьшить ненужные операции блокировки и блокировки, расширяйте несколько непрерывных замков в блокировку с большим диапазоном.
Устранение блокировки: с помощью анализа побега с помощью компилятора JIT выполнения, некоторая защита блокировки устраняется. Некоторые данные, которые не используются другими потоками за пределами текущего блока синхронизации. Благодаря анализу побега, пространство объекта может быть выделено на локальном стеке резьбы (в то же время, оно также может уменьшить накладные расходы на кучу).
LoolweightLocking: реализация этого блокировки основана на предположении, что в реальных случаях большая часть кода синхронизации в нашей программе обычно находится в состоянии конкуренции без блокировки (то есть в среде выполнения с одним поток). В случае соревнования без блокировки он может полностью избежать вызова супертяжелых мутексах на уровне операционной системы. Вместо этого, в мониторе и мониторексите вам нужно полагаться только на атомную инструкцию CAS, чтобы завершить приобретение и освобождение блокировки. При соревнованиях блокировки, поток, который не выполняет инструкции CAS, вызовет операционную систему Mutex, чтобы ввести состояние блокировки и проснуться при выпуске блокировки (конкретные шаги обработки подробно обсуждаются ниже).
BiasedLocking: Это должно избежать выполнения ненужных атомных инструкций CAS во время приобретения блокировки в случае соревнования без блокировки, потому что, хотя инструкции по атомным вопросам CAS относительно невелики по стоимости по сравнению с замками в тяжелом весе, у них по-прежнему очень значительные местные задержки (см. Эта статья).
Адаптивное спиннинг: когда нить не может выполнять работу CAS во время сбора легкого блокировки, он вводит в оживленное ожидание перед введением в операционную систему в тяжелом весе (MutexSemaphore), связанный с монитором, а затем попробуйте еще раз. Если он по -прежнему терпит неудачу после определенного количества попыток, семафор, связанный с монитором (то есть, Mutex Lock), вызван для входа в состояние блокировки.
3. objectheader:
При создании объекта в JVM два заголовка объекта размером с слова будут добавлены перед объектом. Одно слово на 32-битной машине составляет 32 бит. Различное содержание хранится в Markworld в соответствии с различными статусными битами. Как показано на приведенном выше рисунке, в легком блоке Markword разделен на две части. В начале Lockword устанавливается на хэшкод, а самые низкие три бита представляют состояние, где находится Lockword. Первоначальное состояние 001 представляет состояние без блокировки. Klassptr указывает на адрес, представленный объектом, чей байт -код класса находится внутри виртуальной машины. Поля представляют поля экземпляра непрерывного объекта.
4. Монитореректор:
Монитореректор - это личная структура данных потоков. В каждом потоке есть список доступных мониторекордов и глобальный доступный список. Так каково использование этих мониторных окон? Каждый заблокированный объект будет связан с монитореропидом (Lockword в заголовке объекта указывает на начальный адрес монитореропита. Поскольку этот адрес выравнивается 8byte, самые низкие три бита Lockword можно использовать в качестве битов статуса). В то же время в мониторекоррекорде есть поле владельца для хранения уникального идентификатора потока, которому принадлежит блокировка, что указывает на то, что блокировка занята этим потоком. На следующем рисунке показана внутренняя структура монитореропической системы:
Владелец: NULL в начале означает, что ни один поток в настоящее время не владеет записью монитора. Когда поток успешно владеет блокировкой, он сохраняет уникальную идентичность потока, и когда блокировка выпускается, он устанавливается на NULL;
IntryQ: свяжите систему Mutex (Semaphore), чтобы заблокировать все потоки, которые не заблокируют запись монитора.
RcThis: представляет количество всех потоков, заблокированных или ожидающих записи монитора.
Гнездо: используется для реализации подсчета блокировки повторного входа.
HashCode: сохраняет значение HashCode, копируемое из заголовка объекта (также может содержать возраст GC).
Кандидат: используется, чтобы избежать ненужного блокировки или ожидания, когда просыпаются потоки, потому что только один поток может успешно владеть блокировкой каждый раз. Если предыдущий поток, который высвобождает блокировку, каждый раз разбудит все блокирующие или ожидающие резьбы, это приведет к ненужному переключению контекста (от блокировки до готовности, а затем снова блокируется из -за сбоя блокировки конкуренции) и, таким образом, приведет к серьезному деградации производительности. Кандидат имеет только два возможных значения: 0 означает, что нет нити, который необходимо разбудить до 1 средств, чтобы разбудить поток преемника, чтобы конкурировать за блокировку.
5. Конкретная реализация легких замков:
Поток может заблокировать объект двумя способами: 1. Получить блокировку объекта, расширяя объект в состоянии без блокировки (бит состояния 001); 2. Объект уже находится в расширенном состоянии (бит состояния 00), но поле владельца записи монитора, на которую указано Lockword, является нулевым, поэтому вы можете напрямую попытаться установить владельца на свою личность через атомную инструкцию CAS для получения блокировки.
Общий процесс получения блокировки (монитор) выглядит следующим образом:
(1) Когда объект находится в состоянии без блокировки (значение записного слова-это хэшкод, бит состояния равен 001), поток сначала получает бесплатную запись монитора из ее доступного списка записей монитора. Первоначальные значения гнезда и владельца задают 1 и собственную идентификацию потока соответственно. После того, как запись монитора будет готова, мы устанавливаем начальный адрес записи монитора в поле Lockword заголовка объекта через атомную инструкцию CAS для расширения (исходный текст раздувается. Я думаю, что причина, по которой он называется Invate, в основном потому, что объект расширяется после того, как он расширяется; для пространственной эффективности, монитор используется, чтобы расширить размер объекта, который необходимо подключить объект, который необходимо подключить объект. Противоречиво этой статье.
(2) Объект был расширен, и поток, сохраненный у владельца, идентифицируется как поток, который приобретает саму блокировку. Это случай повторных замков. Вам просто нужно просто добавить 1 в гнездо. Никакая атомная операция не требуется и очень эффективна.
(3) Объект был расширен, но значение владельца равна нулю. Это состояние возникает, когда блокировка или ожидание на блокировке заблокирована одновременно, предыдущий владелец блокировки только что выпустил замок. В настоящее время несколько потоков пытаются установить владельца на свою личность через атомную инструкцию CAS, чтобы получить блокировку в многопоточном состоянии конкуренции. Поток, который не может конкурировать, введет путь выполнения четвертого случая (4).
(4) Объект находится в расширенном состоянии, а владелец не является нулевым (заблокирован). Он вращается определенным количеством раз, прежде чем вызовать тяжелую мутекс операционной системы. Когда достигается определенное количество раз, если замок все еще не получен успешно, пришло время начать входить в состояние блокировки. Во -первых, добавьте значение rfThis по -атомно по 1. Поскольку другие потоки могут уничтожить взаимосвязь между объектом и записи монитора во время добавления 1, необходимо выполнить другое сравнение после добавления 1, чтобы убедиться, что значение Lockword не было изменено. Когда обнаружено, что это было изменено, процесс мониторинга должен быть повторен. В то же время наблюдается снова, является ли владелец нулевым. Если это так, это позвонит CAS для участия в соревнованиях. Если соревнование блокировки не удастся, он войдет в состояние блокировки.
Общий процесс выпуска блокировки (мониторексит) выглядит следующим образом:
(1) Сначала проверьте, находится ли объект в расширенном состоянии, а поток является владельцем блокировки. Если обнаружено, что это неправильно, будет брошено исключение;
(2) Проверьте, превышает ли поле гнезда 1. Если оно больше 1, просто уменьшите гнездо на 1 и продолжайте иметь замок. Если он равен 1, то введите шаг (3);
(3) Проверьте, больше ли rfThis 0, установите владельца на NULL и разбудите блокирующие или ожидающие потока, чтобы попытаться снова приобрести блокировку. Если он равен 0, он войдет в шаг (4)
(4) Установите объект, отпустите блокировку, заменив Lockword объекта обратно на исходное значение HashCode, чтобы выпустить блокировку и поместить запись монитора обратно в поток.
Суммировать
Ссылка: « подробное понимание расширенных функций и лучших практик виртуальной машины Java JVM (Zhou Zhiming) »
Выше приведено все содержание этой статьи по синхронизированному и анализу задач реализации деталей JVM. Я надеюсь, что это будет полезно для всех. Если есть какие -либо недостатки, пожалуйста, оставьте сообщение, чтобы указать это. Спасибо, друзья, за вашу поддержку на этом сайте!