Интерфейс Lock

Все известные реализующие классы:
ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock
public interface Lock

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

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

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

Хотя механизм области видимости для методов и операторов synchronized делает программирование с блокировками монитора намного проще и помогает избежать многих распространённых ошибок программирования, связанных с блокировками, существуют случаи, когда вам нужно работать с блокировками более гибким способом. Например, некоторые алгоритмы для обработки одновременно доступных структур данных требуют использования "последовательного" или "цепного блокирования": вы получаете блокировку узла A, затем узла B, затем освобождаете A и получаете C, затем освобождаете B и получаете D и так далее. Реализации интерфейса Lock позволяют использовать такие методы, позволяя получать и освобождать блокировку в разных областях видимости и позволяя получать и освобождать несколько блокировок в любом порядке.

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

Lock l = ...;
 l.lock();
 try {
   // access the resource protected by this lock
 } finally {
   l.unlock();
 }
При блокировании и разблокировании в разных областях видимости необходимо позаботиться о том, чтобы весь код, выполняемый при удержании блокировки, был защищён конструкциями try-finally или try-catch, чтобы гарантировать освобождение блокировки при необходимости.

Реализации интерфейса Lock предоставляют дополнительные функции по сравнению с использованием методов и операторов synchronized путём предоставления попытки получения блокировки без ожидания (tryLock()), попытки получения блокировки, которая может быть прервана (lockInterruptibly()), и попытки получения блокировки с таймаутом (tryLock(long, TimeUnit)).

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

Обратите внимание, что экземпляры Lock — это обычные объекты и могут сами использоваться в качестве целевого объекта в операторе synchronized. Получение блокировки монитора экземпляра Lock не имеет определённой связи с вызовом каких-либо методов lock() этого экземпляра. Рекомендуется избегать использования экземпляров Lock таким образом, за исключением их собственной реализации, чтобы избежать путаницы.

За исключением случаев, о которых упомянуто, передача значения null для любого параметра приведёт к исключению NullPointerException.

Синхронизация памяти

Все реализации Lock должны обеспечивать ту же семантику синхронизации памяти, что и встроенная блокировка монитора, как описано в главе 17 Спецификации языка Java™ .

  • Успешная операция lock имеет те же эффекты синхронизации памяти, что и успешная операция Lock.
  • Успешная операция unlock имеет те же эффекты синхронизации памяти, что и успешная операция Unlock.
Операции неудачного блокирования и разблокирования, а также рекурсивные операции блокирования/разблокирования не требуют никаких эффектов синхронизации памяти.

Рекомендации по реализации

Три формы получения блокировки (прерываемая, непрерывная и с таймаутом) могут отличаться по своим характеристикам производительности, гарантиям порядка или другим качествам реализации. Кроме того, возможность прерывания текущего получения блокировки может отсутствовать в данном классе Lock. Следовательно, от реализации не требуется точно определять те же гарантии или семантику для всех трёх форм получения блокировки, а также не требуется поддержка прерывания текущего получения блокировки. От реализации требуется чётко задокументировать семантику и гарантии, предоставляемые каждым методом блокировки. Она также должна соблюдать семантику прерывания, определённую в этом интерфейсе, в той степени, в которой поддержка прерывания получения блокировки поддерживается: полностью или только при входе в метод.

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

С тех пор:
1.5
См. также:
ReentrantLock, Condition, ReadWriteLock

Методы

Модификатор и тип Метод Описание
void lock()

Получает блокировку.

void lockInterruptibly()

Получает блокировку, если текущий поток не прерван.

Condition newCondition()

Возвращает новый экземпляр Condition, связанный с этим экземпляром Lock.

boolean tryLock()

Получает блокировку только в том случае, если она свободна в момент вызова.

boolean tryLock​(long time, TimeUnit unit)

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

void unlock()

Освобождает блокировку.

Методы

lock

void lock()

Приобретает блокировку.

Если блокировка недоступна, текущий поток становится неактивным для планирования потоков и находится в ожидании, пока блокировка не будет приобретена.

Рекомендации по реализации

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

lockInterruptibly

void lockInterruptibly()
                throws InterruptedException

Приобретает блокировку, если текущий поток не был прерван.

Приобретает блокировку, если она доступна, и возвращает значение немедленно.

Если блокировка недоступна, текущий поток становится неактивным для планирования потоков и находится в ожидании, пока не произойдет одно из двух событий:

  • Блокировка приобретается текущим потоком;
  • Другой поток прерывает текущий поток, и прерывание приобретения блокировки поддерживается.

Если текущий поток:

  • имеет установленный флаг прерывания при входе в этот метод; или
  • бывает прерван во время приобретения блокировки, и прерывание приобретения блокировки поддерживается,
то генерируется исключение InterruptedException, а флаг прерывания текущего потока сбрасывается.

Рекомендации по реализации

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

Реализация может отдавать предпочтение обработке прерывания перед обычным возвратом метода.

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

Исключения:
InterruptedException - если текущий поток прерывается во время приобретения блокировки (и прерывание приобретения блокировки поддерживается)

tryLock

boolean tryLock()

Приобретает блокировку только в том случае, если она свободна в момент вызова.

Приобретает блокировку, если она доступна, и возвращает значение true. Если блокировка недоступна, этот метод возвращает значение false.

Типичный шаблон использования этого метода:

Lock lock = ...;
 if (lock.tryLock()) {
   try {
     // manipulate protected state
   } finally {
     lock.unlock();
   }
 } else {
   // perform alternative actions
 }
Это использование гарантирует, что блокировка разблокируется, если она была приобретена, и не пытается разблокировать, если блокировка не была приобретена.
Возвращает:
true если блокировка была приобретена и false в противном случае

tryLock

boolean tryLock(long time,
                TimeUnit unit)
         throws InterruptedException

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

Если блокировка доступна, этот метод возвращает значение true. Если блокировка недоступна, текущий поток становится неактивным для планирования потоков и находится в ожидании, пока не произойдет одно из трех событий:

  • Блокировка приобретается текущим потоком;
  • Другой поток прерывает текущий поток, и прерывание приобретения блокировки поддерживается;
  • Заданное время ожидания истекает

Если блокировка приобретена, возвращается значение true.

Если текущий поток:

  • имеет установленный флаг прерывания при входе в этот метод; или
  • бывает прерван во время приобретения блокировки, и прерывание приобретения блокировки поддерживается,
то генерируется исключение InterruptedException, а флаг прерывания текущего потока сбрасывается.

Если заданное время ожидания истекает, возвращается значение false. Если время равно или меньше нуля, метод не будет ожидать вообще.

Рекомендации по реализации

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

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

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

Параметры:
time - максимальное время ожидания блокировки
unit - единица измерения времени для аргумента time
Возвращает:
true если блокировка была приобретена и false если время ожидания истекло до приобретения блокировки
Исключения:
InterruptedException - если текущий поток прерывается во время приобретения блокировки (и прерывание приобретения блокировки поддерживается)

unlock

void unlock()

Освобождает блокировку.

Рекомендации по реализации

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

newCondition

Condition newCondition()

Возвращает новую инстанцию Condition, привязанную к этому экземпляру Lock.

Перед ожиданием условия текущий поток должен владеть блокировкой. Вызов Condition.await() атомарно освобождает блокировку перед ожиданием и повторно приобретает блокировку перед возвратом из ожидания.

Рекомендации по реализации

Точное поведение экземпляра Condition зависит от реализации Lock и должно быть задокументировано этой реализацией.

Возвращает:
Новый экземпляр Condition для этого экземпляра Lock
Исключения:
UnsupportedOperationException - если эта реализация Lock не поддерживает условия

© 1993, 2020, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/locks/Lock.html

Spec-Zone .ru
спецификации, руководства, описания, API