- •Средства пакета 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 Executor Framework (9)
Вот некоторые методы класса Executors:
static |
callable( Runnable task ) |
Возвращает Callable-объект, который после |
Callable<Object> |
|
выполнения данной задачи возвращает null. |
static <T> |
callable( Runnable task, T |
Возвращает Callable-объект, который после |
Callable<T> |
result ) |
выполнения данной задачи возвращает result |
static ThreadFactory |
defaultThreadFactory( ) |
Возвращает фабрику потоков, по умолчанию |
|
|
используемую виртуальной машиной для создания |
|
|
новых потоков. |
static ExecutorService |
newCachedThreadPool( ) |
Создает пул потоков, в котором новые потоки |
|
|
создаются при необходимости, а ранее созданные |
|
|
потоки используются, когда они свободны. |
static ExecutorService |
newCachedThreadPool( T |
Создает пул потоков, в котором новые потоки |
|
hreadFactory |
создаются при необходимости с использованием |
|
threadFactory ) |
указанной фабрики, а ранее созданные потоки |
|
|
используются, когда они свободны. |
static ExecutorService |
newFixedThreadPool( int |
Создает пул потоков фиксированного размера. |
|
nThreads ) |
|
java.util.concurrent Executor Framework (10)
static ExecutorService |
newFixedThreadPool( int |
Создает пул потоков фиксированного |
|
nThreads, ThreadFactory |
размера с использованием заданной |
|
threadFactory ) |
фабрики потоков. |
static |
newScheduledThreadPool( int |
Создает пул потоков, реализующих |
ScheduledExecutorService |
corePoolSize ) |
интерфейс управляемого запуска задач. |
static |
newScheduledThreadPool( int |
Создает пул потоков, реализующих |
ScheduledExecutorService |
corePoolSize, ThreadFactory |
интерфейс управляемого запуска задач с |
|
threadFactory ) |
использованием заданной фабрики потоков. |
static ExecutorService |
newSingleThreadExecutor( ) |
Создает исполнителя, который использует |
|
|
единственный рабочий поток. |
static ExecutorService |
newSingleThreadExecutor( Thr |
Создает исполнителя, который использует |
|
eadFactory threadFactory ) |
единственный рабочий поток, создаваемый |
|
|
с использованием заданной фабрики |
|
|
потоков. |
java.util.concurrent Executor Framework (11)
static |
newSingleThreadScheduledExec |
Создает исполнителя, который |
ScheduledExecutorService |
utor( ) |
использует единственный рабочий |
|
|
поток для управляемого запуска задач. |
static |
newSingleThreadScheduledExec |
Создает однопоточного исполнителя для |
ScheduledExecutorService |
utor( ThreadFactory |
управляемого запуска задач с |
|
threadFactory ) |
использованием заданной фабрики |
|
|
потоков. |
static ThreadFactory |
privilegedThreadFactory( ) |
Возвращает фабрику потоков, у которых |
|
|
есть те же самые полномочия, как у |
|
|
текущего потока. |
static ExecutorService |
unconfigurableExecutorService( |
Возвращает объект, который делегирует |
|
ExecutorService executor ) |
данному исполнителю только методы |
|
|
данного ExecutorService. Не могут быть |
|
|
вызваны никакие другие методы. |
static |
unconfigurableScheduledExecuto Возвращает объект, который делегирует |
|
ScheduledExecutorService |
rService( ScheduledExecutorServ данному исполнителю только методы |
|
|
ice executor ) |
данного ScheduledExecutorService. Не |
|
|
могут быть вызваны никакие другие |
|
|
методы. |
java.util.concurrent
Расширение фреймворка Executor: ForkJoinPool
Это расширение разработано специально для упрощения распараллеливания рекурсивных задач.
Краткое описание используемых типов:
ForkJoinTask – это абстрактный класс, который является легковесным аналогом потока (Thread). Благодаря методам, реализованным в классе ForkJoinPool, можно в небольшом
количестве потоков выполнить существенно большее число задач. Это достигается путём так называемого work-stealing'а, когда поток спящей задачи на самом деле не спит, а выполняет другие задачи. У класса ForkJoinTask есть много интересных методов (аналогичных методам invoke* и submit), здесь рассмотрим только два:
fork(), который производит асинхронный запуск задачи
join(), который дожидается выполнения задачи и возвращает результат её выполнения.
Сам по себе класс ForkJoinTask практически не используется, потому что для подавляющего большинства задач уже есть готовые более конкретные реализации:
RecursiveAction, являющийся аналогом Runnable-объектов (на случай, если никакого значения возвращать не нужно, а нужно лишь выполнить некоторое действие), и
RecursiveTask, являющийся аналогом Callable-объектов.
ForkJoinPool – в этом классе как раз и реализована достаточно хитрая логика по распределению нагрузки между реальными потоками. Снаружи он выглядит практически как
самый обычный пул потоков, и особенностей в его использовании по сравнению с базовым ExecutorService нет (он наследует от класса AbstractExecutorService, реализующего этот
интерфейс). Приводить детальный обзор методов класса не будем, вместо этого рассмотрим пример.
java.util.concurrent
Расширение фреймворка Executor: ForkJoinPool. Пример (1)
// существо примера – рекурсивный обход дерева. Здесь пока определяется само дерево interface Node {
Collection<Node> getChildren( ); long getValue( );
}
class MyTree implements Node {
private Collection<Node> childrens = new ArrayList<Node>( );// коллекция дочерних узлов
private long value = 0; |
// этим моделируется содержание узла дерева |
public MyTree( long value ) { |
// конструктор |
this.value = value; |
|
} |
|
public Collection<Node> getChildren( ) { |
// возврат корней поддеревьев |
return childrens; |
|
} |
|
public long getValue( ) { |
|
return value; |
|
} |
|
public void add( MyTree newNode ) { |
// добавление корня нового поддерева |
childrens.add( newNode ); |
|
} |
|
}
java.util.concurrent
Расширение фреймворка Executor: ForkJoinPool. Пример (2)
// это класс, обеспечивающий многопоточный рекурсивный обход дерева class ValueSumCounter extends RecursiveTask<Long> {
private final Node node;
public ValueSumCounter( Node node ) { // конструктор this.node = node;
} |
|
@Override |
|
protected Long compute( ) { |
// основной метод класса RecursiveTask |
long sum = node.getValue( ); |
// для накопления результата |
List<ValueSumCounter> subTasks = new LinkedList<>( ); // коллекция (список) подзадач for( Node child : node.getChildren( ) ) { // обход всех поддеревьев
ValueSumCounter task = new ValueSumCounter( child );// для каждого – своя подзадача task.fork( ); // которую запустим асинхронно
subTasks.add( task ); // и добавим в список для ожидания
}
for( ValueSumCounter task : subTasks ) { // переберем все подзадачи sum += task.join( ); // дождёмся выполнения подзадачи и прибавим ее результат
}
return sum; // вернем собственный результат
}
}
java.util.concurrent
Расширение фреймворка Executor: ForkJoinPool. Пример (3)
public class Main {
public static void main( String[ ] args ) { |
|
|
|
1 |
|
|
|
|||
MyTree root = new MyTree( 1 ); |
// создадим корень |
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
||
MyTree tmp = new MyTree( 2 ); |
// создадим поддерево |
|
|
|
|
|
|
|
|
|
|
2 |
|
|
|
3 |
|||||
root.add( tmp ); // добавим его |
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
root.add( new MyTree( 3 ) ); |
// добавим второе поддерево |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
tmp.add( new MyTree( 4 ) ); |
// добавим узлы |
4 |
|
|
|
5 |
|
|
|
|
tmp.add( new MyTree( 5 ) ); |
// в первое поддерево |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
// выполним обход всего дерева с использованием рекурсивной задачи: long result = new ForkJoinPool( ).invoke( new ValueSumCounter( root ) ); System.out.println( "Результат обхода: " + result );
}
}
Результат обхода: 15
Реализовывать обход дерева таким образом существенно удобнее и интуитивно- понятнее, чем было бы с использованием интерфейса Future<T> и прямым
программированием создания потоков.
Более того, для выполнения fork-нутой задачи вовсе не обязательно будет использоваться выделенный настоящий поток. Напротив, будут активно использоваться уже существующие потоки, которые в текущий момент находятся в join-е. Это, очевидно, может дать существенный прирост производительности, поскольку создание потоков – это времяемкий процесс.
java.util.concurrent Еще одно расширение
фреймворка Executor:
Класс ThreadPoolExecutor
Представляет собой гибко настраиваемый пул потоков, обеспечивающий динамическое управление числом потоков, запуск и управление набором задач, сбор статистики
•Настраивается максимальное и базовое число потоков в пуле, а также время через которое удаляются простаивающие потоки
•Настраивается очередь задач, ожидающих выполнения
•Обработчики, которые вызываются перед запуском или после завершения отдельных задач
Для выполнения всевозможных настроек используются внутренние классы:
•
•
•
•
ThreadPoolExecutor.AbortPolicy
ThreadPoolExecutor.CallerRunsPolicy
ThreadPoolExecutor.DiscardOldestPolicy
ThreadPoolExecutor.DiscardPolicy
java.util.concurrent.locks
Блокировки
До версии Java 1.5 мониторы и методы wait, notify, notifyAll были единственным доступным механизмом блокировок. Начиная с этой версии
появился новый механизм - блокировки, реализованные в виде набора классов и интерфейсов пакета java.util.concurrent.locks.
В отличие от мониторов они не обладают специфичным синтаксисом и не встроены в язык. Их использование осуществляется аналогично любым другим объектам Java. Но они обеспечивают несравненно большую гибкость в отличие от старых методов синхронизации.
При этом средства пакета java.util.concurrent.locks обеспечивают ту же семантику операций с памятью, что и мониторы.
Базовые операции с блокировками описывает интерфейс java.util.concurrent.locks.Lock. В отличие от мониторов функциональность
блокировок из стандартной библиотеки не ограничивается методами безусловного захвата блокировки, которые блокируются на бесконечное время. Существуют методы, обеспечивающие условные попытки захвата и попытки захвата с указанием таймаута.
java.util.concurrent.locks Блокировки (1)
Типичная схема использования нового механизма блокировок выглядит так:
lockObject.lock(); try {
//cинхронизируемые действия
}finally {
lockObject.unlock();
}
где lockObject – это объект класса, реализующего интерфейс java.util.concurrent.locks.Lock.
