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

Средства пакета java.util.concurrent

В Java версии 1.5 был добавлен новый пакет, содержащий много полезных возможностей, касающихся синхронизации и параллелизма: java.util.concurrent.

Возможности этого пакета были развиты и дополнены в версиях 1.6 и 1.7.

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

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

Новые возможности в реализации синхронизации и параллелизма можно разделить на следующие группы:

Вспомогательные классы и интерфейсы Executor Framework

Lock объекты Синхронизаторы

Классы, реализующие атомарные операции Параллельные коллекции

java.util.concurrent Интерфейс Callable

Интерфейс Callable гораздо лучше подходит для создания задач, предназначенных для параллельного выполнения, чем интерфейс Runnable или тем более класс Thread:

import java.util.concurrent.Callable;

public class CallableSample implements Callable<String>{ public String call( ) throws Exception {

if( какое-то условие ) {

throw new Exception("ошибка при выполнении потока");

}

System.out.println("задача выполнена"); return "result";

}

}

При этом стоит отметить, что возможность добавить подобный интерфейс появилась только начиная с версии Java 1.5, так как ключевая особенность интерфейса Callable – это

использование параметризованных типов (generics) для определения типа возвращаемого

результата. В изучаемом дальше фреймворке этот интерфейс реализуется еще более удобным классом FutureTask<V>, обеспечивающим возможность выполнения асинхронных

вычислений в пуле потоков.

Еще одна важная деталь интерфейса Callable: возможность из потока выбрасывать исключения, не влияющие на другие задачи. Такие исключения должны перехватываться и обрабатываться порождающим потоком.

java.util.concurrent Executor Framework (1)

Пакет java.util.concurrent содержит три Executor-интерфейса:

Executor ExecutorService

ScheduledExecutorService

и набор классов, реализующих эти интерфейсы.

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

Интерфейс Executor имеет единственный метод execute(Runnable task). Он позволяет абстрагироваться от того, как конкретно исполняется задача (этим занимается экземпляр класса, реализующего интерфейс):

Executor executor = <…>; executor.execute( new Runnable myTask() );

В пакете java.util.concurrent интерфейс Executor и его расширения реализуют классы ForkJojnPool, ThreadPoolExecutor и SheduledThreadPoolExecutor.

Значительно большие возможности предоставляет интерфейс ExecutorService.

java.util.concurrent Executor Framework (2)

Интерфейс ExecutorService является расширением интерфейса Executor и добавляет следующие полезные возможности:

Возможность выполнения не только Runnable объектов, но и объектов, реализующих новый интерфейс java.util.concurrent.Callable. Основное отличие от Runnable объектов

– возможность возвращать значение из потока.

Возможность возвращать вызывавшему потоку объект класса java.util.concurrent.Future, который содержит среди прочего и возвращаемое значение.

Возможность остановить выполняемый поток.

Для запуска на исполнение одной задачи имеется группа перегруженных методов:

Future<?> submit( Runnable task )

TFuture<T> submit( Runnable task, T result )

TFuture<T> submit( Callable<T> task )

Здесь под T понимается объектный (ссылочный) тип, значение которого будет прямо возвращать задача, если она реализует интерфейс Callable и которое может быть получено от задачи косвенными путями, если она реализует интерфейс Runnable.

Интерфейс Future определяет методы доступа к состоянию и результату задачи: boolean cancel( boolean mayInterruptIfRunning ) Пытается остановить выполнение задачи.

Vget( ) Ожидает завершения задачи и возвращает ее результат.

Vget( long timeout, TimeUnit unit ) Ожидает завершения задачи в течение заданного интервала времени и возвращает ее результат, если он доступен (иначе – null).

boolean isCancelled( ) возвращает истину, если задача была завершена принудительно. boolean isDone( ) возвращает истину, если задача завершилась нормально.

java.util.concurrent Executor Framework (3)

Вот простой пример:

public class CallableImpl implements Callable { public Integer call( ) {

//… выполнение задачи

return new Integer( someValue );

}

}

//…

Callable callable = new CallableImpl( );

ExecutorService executor = Executors.newCachedThreadPool( ); Future future = executor.submit( callable );

try {

System.out.println( "Future value: " + future.get( ) ); } catch ( Exception e ) {

e.printStackTrace();

}

Здесь создается пул потоков не фиксированного размера.

java.util.concurrent Executor Framework (4)

Интерфейс ExecutorService имеет еще две группы методов запуска задач, позволяющих оперировать с целыми группами (коллекциями):

<T> List<Future<T>> invokeAll( Collection<? extends Callable<T>> tasks )

Выполняет все заданные задачи, содержащиеся в коллекции tasks, возвращая список экземпляров Future, и заканчивается, когда все задачи завершаются.

<T> List<Future<T>> invokeAll( Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit )

Выполняет все заданные задачи и заканчивается, когда все завершаются, или истекает тайм-аут заданный в единицах времени unit.

<T> T invokeAny( Collection<? extends Callable<T>> tasks )

Выполняет все заданные задачи, возвращая результат какой-либо из тех, которые завершились успешно (то есть, не выбросили исключение).

<T> T invokeAny( Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit )

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

Перечисление TimeUnit определяет константы DAYS, HOURS, MINUTES, MICROSECONDS, MILLISECONDS, NANOSECONDS, SECONDS и набор полезных методов для работы с временными интервалами.

java.util.concurrent Executor Framework (5)

Интерфейс ExecutorService кроме того содержит методы, позволяющие проверять состояние задач, инициировать их завершение и ожидать этого завершения:

boolean awaitTermination( long timeout, TimeUnit unit )

Блокирует текущий поток до завершения всех задач, или истечения заданного тайм-аута, или прерывания текущего потока – что произойдет раньше.

boolean isShutdown( )

возвращает true, если этот исполнитель задач остановлен. boolean isTerminated( )

возвращает true, если все задачи завершились. void shutdown( )

Инициирует аккуратное завершение работы. При этом все ранее запущенные

задачи выполняются до завершения, но никакие новые (путем вызова методов submit или invoke*) задачи экземпляром класса, реализующего ExecuteService, не

будут приняты.

List<Runnable> shutdownNow( )

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

java.util.concurrent Executor Framework (6)

Интерфейс SheduledExecutorService расширяет интерфейс ExecutorService путем добавления методов, управляющих планированием запуска задач через заданный интервал времени и/или через заданные промежутки времени:

<V> ScheduledFuture<V> schedule(Callable<V> task, long delay, TimeUnit unit)

Создает задачу, представленную task, которая становится активной после заданной задержки и возвращает экземпляр ScheduledFuture (расширенный Future).

ScheduledFuture<?> schedule(Runnable task, long delay, TimeUnit unit)

Создает задачу, представленную task, которая становится активной после заданной

задержки.

ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit)

Создает и выполняет периодическую задачу, которая становится активной сначала после данной начальной задержки с установленным сроком (выполнение начнется после initialDelay в момент initialDelay+period, затем initialDelay + 2 * period, и так далее)

ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay, long delay, TimeUnit unit)

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

java.util.concurrent Executor Framework (7)

Вот простейший пример использования интерфейса ScheduledExecutorService:

import java.util.concurrent.*; public class Beep {

public static void main( String[ ] args ) {

// создать планируемый пул потоков размером 1 поток ScheduledExecutorService ses = Executors.newScheduledThreadPool( 1 ); Runnable beeper = new Runnable( ) { // анонимный внутренний класс

public void run( ) { System.out.println("Beep!");

}

};

// выполнять beeper каждые 3 секунды после начальной 10-секундной задержки ses.scheduleAtFixedRate( beeper, 10, 3, TimeUnit.SECONDS );

}

}

Есть класс ScheduledThreadPoolExecutor, реализующий данный интерфейс.

java.util.concurrent Executor Framework (8)

В предыдущих примерах неоднократно упоминался класс Executors, являющийся фабрикой для интерфейсов фреймворка и содержащий группы статических методов:

Которые создают и возвращают экземпляры ExecutorService с обычными параметрами конфигурации.

Которые создают и возвращают экземпляры ScheduledExecutorService с обычными параметрами конфигурации.

Которые создают и возвращают "обернутые" экземпляры ExecutorService, для которых отключено реконфигурирование и становятся недоступными некоторые специфичные методы.

Которые создают и возвращают фабрику потоков ThreadFactory, используемую виртуальной машиной по умолчанию для создания новых потоков.

Которые создают и возвращают объекты Callable из экземпляров других классов (таких как Runnable).

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

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

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

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