Что является монитором при вызове синхронизированного статического метода
Синхронный метод получает монитор (§17.1) перед его выполнением.
Для класса (статического) метода используется монитор, связанный с объектом класса для класса метода.
В этом синхронизированном методе перемещается блокировка уровня класса. Итак, какой объект класса точно используется для получения этой блокировки. Это Interface.class или ClassImplementingInterface.class ? Если это более поздний, есть ли какой-нибудь сценарий, где мы можем иметь интерфейсные мониторы? или, скорее, интерфейс имеет монитор?
Я прочитал, что каждый объект связан с монитором, а в случае мониторинга статических замков получается соответствующий объект класса. Как мы можем сделать Interface.Class, что означает, что интерфейс имеет соответствующий объект класса, мы можем получить блокировку на этом мониторе без явного указания synchronized(Interface.class) .
interface является типом и поэтому может получить свой объект Class<?>
Тем не менее, для Java 7 и раньше, ваш вопрос является спорным, поскольку интерфейсы не могут иметь статические методы и статические методы не могут быть переопределены, поэтому он всегда будет получить Class объект класса его называют на.
Вызов StaticTest.test() будет синхронизироваться с объектом StaticText.class .
Что касается, например, методов с synchronized модификатором, это снова будет наблюдаемый объект (блокировка) вызывающего объекта, который вы приобретете.
В Java 8 вы можете объявлять и определять static методы в интерфейсах, но они не могут быть изменены с помощью synchronized ключевого слова.
Это снова не проблема.
есть ли какой-либо сценарий, где мы можем иметь интерфейсные мониторы
не с synchronized модификатором. Но вы всегда можете использовать синхронизированный блок для объекта Class интерфейса.
но на самом деле, зачем вы это делаете? Я думаю, что это будет путать для людей, читающих ваш код.
Бывают случаи, когда два или более параллельно-выполняемых потока пытаются обратиться к общему ресурсу. Если ресурс может быть изменен в результате выполнения одного из потоков, то другие потоки должны дождаться пока изменения в потоке будут завершены. В противном случае, потоки получат ресурс, данные которого будут ошибочными.
Поток выполнения (который представлен объектом) может завладеть монитором в случае, если он запросил блокировку и монитор свободен на данный момент. После того, как объект вошел в монитор, все остальные объекты-потоки, пытающиеся войти в монитор, приостанавливаются и ожидают до тех пор, пока первый объект не выйдет из монитора.
Монитором может обладать только один поток. Если поток (объект) обладает монитором, то он при необходимости может повторно войти в него.
В языке Java синхронизация применяется к целым методам или фрагментам кода. Исходя из этого существует два способа синхронизации программного кода:
- за счет использования модификатора доступа synchronized ;
- за счет использования оператора synchronized () <> .
2. Модификатор доступа synchronized . Общая форма
Модификатор доступа synchronized применяется при объявлении синхронизированного метода и имеет следующую общую форму:
- MethodName – имя метода, который есть общим ресурсом и его нужно синхронизировать;
- return_type -тип, который возвращает метод;
- parameters – параметры метода.
3. Оператор synchronized() < >. Общая форма
Оператор synchronized() , в отличие от модификатора доступа synchronized , используется для применения синхронизации к объектам (методам), которые изначально при их разработке не предназначались для многопоточного доступа. То есть, в классе не реализованы методы с модификатором доступа synchronized (классы сторонних разработчиков).
Общая форма оператора synchronized () следующая:
- reference – ссылка на синхронизируемый объект;
- фигурные скобки <> , которые определяют блок синхронизации. В этом блоке указываются операторы для синхронизации.
4. Пример, демонстрирующий синхронизированный доступ к общему методу из трех разных потоков. Применение модификатора доступа synchronized
В примере демонстрируется необходимость применения модификатора доступа synchronized с целью упорядочения доступа к ресурсу из разных потоков.
Общим ресурсом является метод Get() класса Array5. Метод Get() возвращает массив из 5 чисел, значения которых последовательно изменяются от 1 к 5. К этому методу будет осуществлена попытка одновременного доступа из разных потоков, поэтому метод обозначен как synchronized .
Инкапсуляция одного потока выполнения осуществляется в классе ArrayThread классическим (стандартным) способом с помощью реализации интерфейса Runnable . Класс ArrayThread содержит следующие общедоступные ( public ) элементы:
В классе Threads представлена функция main() , в которой создается три потока AT1 , AT2 , AT3 типа ArrayThread и один объект A5 . Метод Get() объекта A5 является общим ресурсом для этих трех потоков.
Результат выполнения программы
Если в вышеприведенном примере перед методом Get() класса Array5 убрать ключевое слово synchronized
то последовательного выполнения потоков не будет. В этом случае программа после каждого запуска будет выдавать разный (хаотический) результат, например следующий
5. Пример использования оператора synchronized() <> для синхронизированного доступа к общему ресурсу
Код предыдущего примера (смотрите п. 4), синхронизирующего потоки, можно представить по-другому. В этом случае, вместо модификатора доступа synchronized перед именем метода Get() нужно использовать оператор synchronized () <> . Оператор synchronized должен быть использован в методе run() класса ArrayThread .
Привет! Сегодня продолжим рассматривать особенности многопоточного программирования и поговорим о синхронизации потоков.
Как работает оператор synchronized в Java
Все потоки, принадлежащие одному процессу, разделяют некоторые общие ресурсы (адресное пространство, открытые файлы). Что произойдет, если один поток еще не закончил работать с каким-либо общим ресурсом, а система переключилась на другой поток, использующий тот же ресурс?
Когда два или более потоков имеют доступ к одному разделенному ресурсу, они нуждаются в обеспечении того, что ресурс будет использован только одним потоком одновременно. Процесс, с помощью которого это достигается, называется синхронизацией.
Пример 1. Одновременный доступ к ресурсу
Два потока в предыдущем примере находятся в состоянии гонок. Состояние гонок – это одновременный вызов в потоках исполнения одного и того же метода для того же самого объекта.
Чтобы защитить данные, нам необходимо выполнить два действия:
- Объявить переменные как private.
- Синхронизировать код.
2. Способы синхронизации кода
Синхронизировать прикладной код можно двумя способами:
- С помощью синхронизированных методов. Метод объявляется с использованием ключевого слова synchronized:
- Заключить вызовы методов в блок оператора synchronized:
Только методы и блоки могут быть синхронизированы, но не переменные и классы.
Не все методы в классе должны быть синхронизированы.
3. Модификатор volatile
Поток создается с чистой рабочей памятью, и должен перед использованием загрузить все необходимые переменные из основного хранилища (можно сказать что он имеет некий кэш).
Любая переменная сначала создается в основном хранилище и лишь затем копируется в рабочую память потоков, которые будут ее применять.
Если переменная объявлена, как volatile, то ее чтение и запись будет производиться из\в основное хранилище.
Чтение volatile переменных синхронизировано и запись в volatile переменные синхронизирована, а неатомарные операции – нет.
4. Монитор
Каждый объект в Java имеет ассоциированный с ним монитор. Монитор - это объект, используемый в качестве взаимоисключающей блокировки. Когда поток исполнения запрашивает блокировку, то говорят, что он входит в монитор.
Только один поток исполнения может в одно, и то же время владеть монитором. Все другие потоки исполнения, пытающиеся войти в заблокированный монитор, будут приостановлены до тех пор, пока первый поток не выйдет из монитора. Говорят, что они ожидают монитор.
Поток, владеющий монитором, может, если пожелает, повторно войти в него.
Если поток засыпает, то он удерживает монитор.
Поток может захватить сразу несколько мониторов.
Рассмотрим разницу между доступом к объекту без синхронизации и из синхронизированного кода. Доступ к банковскому счету без синхронизации:
И с синхронизацией:
Когда выполнение кода доходит до оператора synchronized, монитор объекта счет блокируется, и на время его блокировки монопольный доступ к блоку кода имеет только один поток, который и произвел блокировку (Люси).
После окончания работы блока кода, монитор объекта счет освобождается и становится доступным для других потоков.
После освобождения монитора его захватывает другой поток, а все остальные потоки продолжают ожидать его освобождения.
Синхронизируем методы из предыдущего примера для корректного совместного доступа потоков:
Пример 2. Синхронизация доступа к ресурсу
5. Синхронизация статических методов
Статические методы тоже могут быть синхронизированы с помощью ключевого слова synchronized.
Для синхронизации статических методов используется один монитор для одного класса. Каждый загруженный в Java класс имеет соответствующий объект класса Class, представляющий этот класс. Монитор именно этого объекта используется для синхронизации статических методов (если они синхронизированы).
Пример 3. Синхронизация статических методов
И эквивалентный код:
6. Блокировка
Если поток пытается зайти в синхронизированный метод, а монитор уже захвачен, то поток блокируется по монитору объекта.
Поток попадает в специальный пул для этого конкретного объекта и должен находиться там пока монитор не будет освобожден. После этого поток возвращается в состояние runnable.
Ключевое слово Java synchronized используется в многопоточности для создания блока кода, который может выполняться только одним потоком одновременно.
Зачем нам нужна синхронизация?
Когда у нас есть несколько потоков, работающих над общим объектом, конечный результат может быть поврежден. Допустим, у нас есть простая программа для увеличения переменной счетчика объекта. Эта переменная является общей для всех потоков.
Пример синхронизированного ключевого слова Java
Как синхронизированное ключевое слово работает внутри компании?
Логика синхронизации Java построена вокруг внутренней сущности, называемой встроенной блокировкой или блокировкой монитора .
Когда поток пытается войти в синхронизированную область, он должен сначала получить блокировку объекта. Затем выполняются все операторы в синхронизированном блоке. Наконец, поток освобождает блокировку объекта, который может быть получен другими потоками в пуле ожидания.
Блок синхронизации Java
Когда блок кода обернут вокруг ключевого слова synchronized, он называется синхронизированным блоком.
Синтаксис синхронизированного блока
Вот простой пример синхронизированного блока в Java.
Метод синхронизации Java
Иногда требуется синхронизировать каждый оператор внутри метода. В этом случае мы можем синхронизировать сам метод.
Синтаксис синхронизированного метода
Вот пример метода синхронизации java.
Блокировка объекта в Синхронизированном методе
Как и синхронизированный блок, синхронизированные методы также требуют блокировки объекта.
- Если метод статический , блокировка приобретается для класса.
- Если метод нестатичен, блокировка будет получена для текущего объекта.
Метод синхронизации Java против блока
- Метод синхронизации Java блокирует текущий объект, поэтому, если существует другой синхронизированный метод, другие потоки будут ожидать блокировки объекта, даже если в этих методах нет общей переменной. Синхронизированный блок Java работает с полем объекта, поэтому в этом случае лучше использовать синхронизированный блок.
- Если объект имеет несколько синхронизированных методов, работающих с одними и теми же переменными, то предпочтителен синхронизированный метод. Например, StringBuffer использует синхронизированные методы, поскольку все методы append() и insert() работают с одним и тем же объектом.
Вот пример, когда у нас есть несколько методов, работающих с одной и той же переменной, поэтому использование синхронизированного метода является лучшим выбором.
Вот еще один пример, когда различные методы работают с другой общей переменной, поэтому использование синхронизированного блока является лучшим выбором.
Вывод
Ключевое слово Java synchronized полезно для предотвращения повреждения данных при многопоточном программировании. Однако синхронизация снижает производительность кода из-за дополнительных накладных расходов на механизм блокировки. Использование синхронизированного блока или синхронизированного метода во многом зависит от требований вашего проекта.
Читайте также: