1. Обзор
В этом руководстве мы рассмотрим двоичные семафоры и повторные блокировки. Кроме того, мы сравним их друг с другом, чтобы увидеть, какой из них лучше всего подходит для обычных ситуаций.
2. Что такое двоичный семафор?
Бинарный семафор обеспечивает механизм сигнализации при доступе к одному ресурсу. Другими словами, бинарный семафор обеспечивает взаимное исключение, позволяющее только одному потоку одновременно обращаться к критическому разделу .
Для этого он сохраняет только одно разрешение для доступа. Следовательно, бинарный семафор имеет только два состояния: одно доступное разрешение или нулевое доступное разрешение .
Давайте обсудим простую реализацию бинарного семафора с использованием класса Semaphore
, доступного в Java:
Semaphore binarySemaphore = new Semaphore(1);
try {
binarySemaphore.acquire();
assertEquals(0, binarySemaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
binarySemaphore.release();
assertEquals(1, binarySemaphore.availablePermits());
}
Здесь мы можем заметить, что метод получения
уменьшает количество доступных разрешений на единицу. Точно так же метод освобождения
увеличивает количество доступных разрешений на единицу.
Кроме того, класс Semaphore предоставляет параметр
справедливости
. Если установлено значение true
, параметр справедливости
обеспечивает порядок, в котором запрашивающие потоки получают разрешения (в зависимости от времени ожидания):
Semaphore binarySemaphore = new Semaphore(1, true);
3. Что такое реентерабельная блокировка?
Блокировка с повторным входом — это механизм взаимного исключения, который позволяет потокам повторно входить в блокировку на ресурсе (несколько раз) без ситуации взаимоблокировки .
Поток, входящий в блокировку, каждый раз увеличивает счетчик удержания на единицу. Точно так же количество удержаний уменьшается, когда запрашивается разблокировка. Поэтому ресурс блокируется до тех пор, пока счетчик не вернется к нулю .
Например, давайте рассмотрим простую реализацию с использованием класса ReentrantLock
, доступного в Java:
ReentrantLock reentrantLock = new ReentrantLock();
try {
reentrantLock.lock();
assertEquals(1, reentrantLock.getHoldCount());
assertEquals(true, reentrantLock.isLocked());
} finally {
reentrantLock.unlock();
assertEquals(0, reentrantLock.getHoldCount());
assertEquals(false, reentrantLock.isLocked());
}
Здесь метод блокировки
увеличивает счетчик удержания на единицу и блокирует ресурс. Точно так же метод разблокировки
уменьшает количество удержаний и разблокирует ресурс, если счетчик удержаний равен нулю.
Когда поток повторно входит в блокировку, он должен запросить разблокировку столько же раз, чтобы освободить ресурс:
reentrantLock.lock();
reentrantLock.lock();
assertEquals(2, reentrantLock.getHoldCount());
assertEquals(true, reentrantLock.isLocked());
reentrantLock.unlock();
assertEquals(1, reentrantLock.getHoldCount());
assertEquals(true, reentrantLock.isLocked());
reentrantLock.unlock();
assertEquals(0, reentrantLock.getHoldCount());
assertEquals(false, reentrantLock.isLocked());
Подобно классу Semaphore
, класс ReentrantLock
также поддерживает параметр справедливости
:
ReentrantLock reentrantLock = new ReentrantLock(true);
4. Двоичный семафор против реентерабельной блокировки
4.1. Механизм
Бинарный семафор — это тип механизма сигнализации , тогда как реентерабельная блокировка — это механизм блокировки.
4.2. Владение
Ни один поток не является владельцем двоичного семафора. Однако последний поток, успешно заблокировавший ресурс, является владельцем повторной блокировки .
4.3. Природа
Двоичные семафоры по своей природе не допускают повторного входа, что означает, что один и тот же поток не может повторно получить доступ к критической секции, иначе это приведет к тупиковой ситуации.
С другой стороны, реентерабельная блокировка по своей природе допускает повторный вход в блокировку одним и тем же потоком несколько раз.
4.4. Гибкость
Двоичный семафор обеспечивает механизм синхронизации более высокого уровня , позволяя настраивать реализацию механизма блокировки и восстановление взаимоблокировки. Таким образом, это дает больше контроля разработчикам.
Однако реентерабельная блокировка — это низкоуровневая синхронизация с фиксированным механизмом блокировки .
4.5. Модификация
Двоичные семафоры поддерживают такие операции, как ожидание и сигнал (получение и освобождение в случае класса Java Semaphore
), чтобы разрешить модификацию доступных разрешений любым процессом.
С другой стороны, только тот же поток, который заблокировал/разблокировал ресурс, может изменить повторную блокировку.
4.6. Восстановление тупика
Двоичные семафоры обеспечивают механизм освобождения без права собственности . Таким образом, любой поток может освободить разрешение на восстановление двоичного семафора из тупиковой ситуации.
Напротив, в случае повторной блокировки трудно добиться восстановления взаимоблокировки. Например, если поток-владелец реентерабельной блокировки переходит в спящий режим или бесконечное ожидание, освободить ресурс будет невозможно, и возникнет тупиковая ситуация.
5. Вывод
В этой короткой статье мы рассмотрели бинарный семафор и повторные блокировки.
Во-первых, мы обсудили базовое определение бинарного семафора и реентерабельной блокировки, а также базовую реализацию на Java. Затем мы сравнили их друг с другом по нескольким параметрам, таким как механизм, владение и гибкость.
Мы с уверенностью можем заключить, что бинарный семафор обеспечивает не основанный на владении сигнальный механизм для взаимного исключения . В то же время его можно расширить, чтобы обеспечить возможности блокировки с простым восстановлением взаимоблокировки.
С другой стороны, блокировка с повторным входом обеспечивает повторное взаимное исключение с возможностями блокировки на основе владельца и полезна в качестве простого мьютекса.
Как обычно, исходный код доступен на GitHub .