Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
39
Добавлен:
27.03.2015
Размер:
271.87 Кб
Скачать

java.util.concurrent.locks Блокировки (2)

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

Вот методы интерфейса Lock:

void

lock( )

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

void

lockInterruptibly( )

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

 

 

следующий слайд).

Condition

newCondition( )

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

 

 

экземпляром объекта Lock.

boolean

tryLock( )

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

 

 

вызова. В этом случае возвращается значение true.

boolean

tryLock( long time,

Получает блокировку, если объект оказался свободен в рамках

 

TimeUnit unit )

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

 

 

В этом случае возвращается значение true.

void

unlock( )

Освобождает объект (точнее – один его захват).

java.util.concurrent.locks Блокировки (3)

Обычная схема использования метода lockInterruptibly:

Lock myLockMonitor = new ReentrantLock( );

// класс, реализующий интерфейс Lock

try {

 

 

myLockMonitor.lockInterruptibly( );

 

 

try {

 

 

// доступ к защищенному ресурсу

 

 

 

 

} finally {

 

 

} myLockMonitor.unlock( );

// необходимо, потому что блокировка

} catch (InterruptedException e) {

// System.err.println("Interrupted wait");

// может быть прервана

}

Обычная схема использования метода tryLock: if ( myLockMonitor.tryLock( ) ) {

try {// доступ к защищенному ресурсу } finally {

} myLockMonitor.unlock( );

} else//{альтернативное действие

}

java.util.concurrent.locks Блокировки (4)

 

Методы класса ReentrantLock:

int

getHoldCount( )

Возвращает количество захваченных данным потоком

 

 

блокировок

protected Thread

getOwner( )

Возвращает поток, которому в настоящий момент

 

 

принадлежит этот экземпляр, или null если нет такого

 

 

потока.

protected

getQueuedThreads( )

Возвращает набор, содержащий потоки, ожидающие

Collection<Thread>

 

получения блокировки этого экземпляра.

int

getQueueLength( )

Возвращает количество потоков, ожидающих получения

 

 

блокировки этого экземпляра.

protected

getWaitingThreads( C Возвращает набор, содержащий потоки, ожидающие

Collection<Thread>

ondition condition )

освобождения заданного экземпляра интерфейса

 

 

Condition, связанного с этим экземпляром класса

 

 

ReentrantLock.

int

getWaitQueueLength(

Возвращает количество потоков, ожидающих

 

Condition condition )

освобождения заданного экземпляра интерфейса

 

 

Condition, связанного с этим экземпляром класса

 

 

ReentrantLock.

java.util.concurrent.locks Блокировки (5)

boolean

hasQueuedThread( Thread

Возвращает true, если указанный поток ожидает получения

 

thread )

этой блокировки.

boolean

hasQueuedThreads( )

Возвращает true, если есть потоки, ожидающие получения

 

 

этой блокировки.

boolean

hasWaiters( Condition

Возвращает true, если есть потоки, ожидающие получения

 

condition )

блокировки указанного экземпляра Condition.

boolean

isFair( )

Возвращает true если для этого экземпляра блокировки

 

 

установлена политика справедливости (первый запросивший

 

 

поток получит блокировку первым). Политика справедливости

 

 

может быть задана булевым аргументом конструктора

 

 

экземпляра класса ReentrantLock.

boolean

isHeldByCurrentThread( )

Возвращает true, если эта блокировка захвачена текущим

 

 

потоком.

boolean

isLocked( )

Возвращает true, если эта блокировка захвачена каким-либо

 

 

потоком.

void

lock( )

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

Остальные методы класса просто реализуют все методы интерфейса Lock.

java.util.concurrent.locks Блокировки (7)

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

Такое поведение далеко не всегда эффективно. Допустим у нас есть несколько потоков изменения данных и несколько потоков чтения с соотношением количества операций изменения/чтения равным 1/10. В случае использования общей блокировки все конкурирующие потоки будут выполняться строго последовательно на участке доступа к разделяемой памяти. Но ведь все потоки чтения вполне могут иметь одновременный доступ к данным, что позволит им выполняться параллельно. При этом поток, изменяющий данные, при получении ресурса должен оказаться единственным владельцем защищаемого участка памяти.

Нужное поведение поддерживается классом ReentrantReadWriteLock реализующим интерфейс ReadWriteLock. Для пользователя объект данного класса выглядит как

контейнер с двумя методами, возвращающими ссылки на две разные блокировки чтения и записи. Блокировка записи ведёт себя полностью аналогично ReentrantLock, а блокировка

чтения отличается и позволяет нескольким потокам становиться её владельцем. Таким образом для потоков допускается одновременное владение несколькими блокировками чтения, но только одной блокировкой записи.

Методы класса ReentrantReadWriteLock очень похожи на методы класса ReentrantLock, только захват блокировки lock( ) разделен на readLock( ) и writeLock( ).

Классы блокировок, реализующих интерфейс Lock, имеют ещё один метод newCondition, возвращающий объект класса Condition. Применение его полностью

аналогично базовому механизму мониторов, методу wait( ) соответствует метод await( ), методу notify( ) – signal( ), методу notifyAll( ) – signalAll( ).

java.util.concurrent

Синхронизаторы. Семафоры (1)

К синхронизаторам можно отнести различного рода структуры, которые отвечают за координацию работы потоков. Некоторые такие структуры реализованы в пакете java.util.concurrency:

Семафоры

Барьеры

Обменники

Защелки

Считающим семафором называют целочисленную переменную, выполняющую те же функции, что и флаг блокировки. Однако в отличие от последнего она может принимать кроме 0 и 1 ( true/false) и другие целые положительные значения.

Семафоры используются для ограничения числа потоков, которые используют некий ресурс. Максимально возможное значение семафора, понимаемое как количество потоков, которые одновременно могут получить доступ к ресурсу, задается аргументом конструктора. Вторым (необязательным) аргументом может быть булево значение, определяющее "справедливость" захвата ресурса ожидающими потоками. При реализации справедливой политики ресурс будет отдан тому потоку, который первым попытался его захватить. За справедливость приходится платить довольно большими временными затратами, поэтому обычно реализуется несправедливый вариант.

java.util.concurrent

Синхронизаторы. Семафоры (2)

Основные методы класса java.util.concurrent.Semaphore:

void

acquire( )

Получить доступ к ресурсу. Поток блокируется, пока

 

 

не доступ не будет получен или пока его не прервут.

void

acquire( int permits )

Получить permits доступов к ресурсу. Поток

 

 

блокируется, пока не доступ не будет получен или

 

 

пока его не прервут.

void

acquireUninterruptibly()

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

void

acquireUninterruptibly( i

Получить permits доступов к ресурсу с запретом

 

nt permits )

прерывания.

int

availablePermits( )

Возвращает количество свободных доступов

int

drainPermits( )

Получить все свободные доступы

protected

getQueuedThreads( )

Возвращает коллекцию потоков, ожидающих

Collection<Thread>

 

получения доступа

int

getQueueLength( )

Возвращает количество потоков, ожидающих

 

 

получения доступа

boolean

hasQueuedThreads( )

Возвращает true, если есть хотя бы один ожидающий

 

 

поток

java.util.concurrent

Синхронизаторы. Семафоры (3)

boolean

isFair( )

Возвращает true, если для семафора установлена

 

 

справедливая политика

protected

reducePermits( int reduction )

Уменьшить количество доступов на указанную величину

void

 

 

void

release( )

Освободить один доступ

void

release( int permits )

Освободить указанное количество доступов

boolean

tryAcquire( )

Попытаться получить один доступ. В случае успеха

 

 

возвращается true.

boolean

tryAcquire( int permits )

Попытаться получить указанное количество доступов. В

 

 

случае успеха возвращается true.

boolean

tryAcquire( int permits, long

Попытаться получить указанное количество доступов в

 

timeout, TimeUnit unit )

течение заданного интервала времени. В случае успеха (и

 

 

если поток не был прерван) возвращается true.

boolean

tryAcquire( long timeout,

Попытаться получить один доступ в течение заданного

 

TimeUnit unit )

интервала времени. В случае успеха (и если поток не был

 

 

прерван) возвращается true.

java.util.concurrent

Синхронизаторы. Семафоры (4)

Задача "писатель-читатель", в которой для передачи данных используется пул буферов: interface Buffer {

void markAsUsed( ); boolean markedAsUsed( );

}

private final Semaphore buffers = new Semaphore( MAX_BUFFERS, true );

public Buffer getBuffer() throws InterruptedException { buffers.acquire();

Buffer buffer = getNextAvailableBuffer(); buffer.markAsUsed( );

return buffer;

}

public void retBuffer(Buffer buf) {

if (buf.markedAsUsed( ) == false) buffers.release( );

putBufferToQueue( buf );

}

java.util.concurrent

Синхронизаторы. Барьеры (1)

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

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

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

В пакете java.util.concurrent для реализации барьерной синхронизации используется класс CyclicBarrier. Конструкторы этого класса получают

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

исполнен в момент достижения этой точки всеми потоками:

CyclicBarrier( int parties );

CyclicBarrier( int parties, Runnable barrierAction );

Соседние файлы в папке Презентации по Java