- •Средства пакета java.util.concurrent
- •java.util.concurrent Интерфейс Callable
- •java.util.concurrent Executor Framework (1)
- •java.util.concurrent Executor Framework (2)
- •java.util.concurrent Executor Framework (3)
- •java.util.concurrent Executor Framework (4)
- •java.util.concurrent Executor Framework (5)
- •java.util.concurrent Executor Framework (6)
- •java.util.concurrent Executor Framework (7)
- •java.util.concurrent Executor Framework (8)
- •java.util.concurrent Executor Framework (9)
- •java.util.concurrent Executor Framework (10)
- •java.util.concurrent Executor Framework (11)
- •java.util.concurrent
- •java.util.concurrent
- •java.util.concurrent
- •java.util.concurrent
- •java.util.concurrent Еще одно расширение
- •java.util.concurrent.locks
- •java.util.concurrent.locks Блокировки (1)
- •java.util.concurrent.locks Блокировки (2)
- •java.util.concurrent.locks Блокировки (3)
- •java.util.concurrent.locks Блокировки (4)
- •java.util.concurrent.locks Блокировки (5)
- •java.util.concurrent.locks Блокировки (7)
- •java.util.concurrent
- •java.util.concurrent
- •java.util.concurrent
- •java.util.concurrent
- •java.util.concurrent
- •java.util.concurrent
- •java.util.concurrent Синхронизаторы.
- •java.util.concurrent
- •java.util.concurrent
- •java.util.concurrent Синхронизаторы.
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 );
