
- •Многопоточное программирование
- •Цели занятия
- •Понятие многопоточности
- •Реализация многопоточности
- •Реализация многопоточности
- •Особенности многопоточности
- •Использование класса Thread
- •Использование класса Thread
- •Использование класса Thread
- •Использование интерфейса
- •Использование интерфейса
- •Управление потоками
- •Нерекомендуемые действия над
- •Группы потоков
- •Группы потоков
- •Операции в группе потоков
- •Приоритеты потоков
- •Приоритеты потоков
- •Демон-потоки
- •Демон-группы потоков
- •Совместное использование
- •Совместное использование
- •Совместное использование
- •Совместное использование
- •Характерные ошибки
- •volatile
- •volatile
- •Специальные методы класса
- •Особенности использования
- •Прерывание потока
- •Пример
- •Пример
- •Пример
- •java.util.concurrent
- •java.util.concurrent
- •java.util.concurrent.locks
- •Lock Objects
- •ReentrantLock
- •Пример ReentrantLock
- •ReadWriteLock
- •Пример ReadWriteLock
- •Исполнители (Executors)
- •Интерфейсы исполнителей
- •Интерфейс Executor
- •Интерфейс
- •Thread Pools
- •Thread Pools
- •Executors
- •Пример ExecutorService
- •Пример ExecutorService.invokeAll()
- •InterruptedException
- •Прерывание потока
- •Пример InterruptedException
- •Пример восстановления статуса
- •Пример отменяемой задачи
- •Литература

ReadWriteLock
41
•ReadWriteLock сохраняет пару связанных блокировок: одну для операции чтения, другую – для записи
•Блокировки чтения могут проводиться одновременно из нескольких потоков чтения, пока нет писателей, а блокировка записи является эксклюзивной
•Все ReadWriteLock реализации должны гарантировать, что синхронизация операций writeLock() (как указано в Lock интерфейсе) справедлива и по отношению к связанной readLock(), то есть поток, успешно наложивший блокировку на чтение, увидит все обновления, сделанные до последнего снятия блокировки на запись
Все права защищены. www.haulmont.ru info@haulmont.com |
© HAULMONT, 2013 |

Пример ReadWriteLock
42
Пример 4
public class Dictionary {
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final Lock read = readWriteLock.readLock();
private final Lock write = readWriteLock.writeLock();
private HashMap<String, String> dictionary = new HashMap<String, String>(); public void set(String key, String value) {
write.lock();
try { dictionary.put(key, value); } finally { write.unlock(); }
}
public String get(String key) { read.lock();
try{ return dictionary.get(key); } finally { read.unlock(); }
}
public String[] getKeys() { read.lock();
try {
String keys[] = new String[dictionary.size()]; return dictionary.keySet().toArray(keys);
}finally { read.unlock(); }
}}
Все права защищены. www.haulmont.ru info@haulmont.com |
© HAULMONT, 2013 |

Исполнители (Executors)
43
•Во всех предыдущих примерах, существует тесная связь между задачей, выполняемой новым потоком и определяемой ее Runnable объектом, и самим потоком
Thread
•Этот подход работает для небольших приложений, но в крупных приложениях имеет смысл отделить управление потоками и их создание от остальной части приложения
•Объекты, внутри которых реализуются эти функции, называются исполнителями:
•Executor Intefaces определяют три типа исполнителей
•Thread Pools являются наиболее общим типом реализации исполнителей
Все права защищены. www.haulmont.ru info@haulmont.com |
© HAULMONT, 2013 |

Интерфейсы исполнителей
44
•Пакет java.util.concurrent содержит три интерфейса
исполнителей:
Executor – простой интерфейс, поддерживающий запуск новой задачи
ExecutorService, расширяющий Executor, добавляет возможности по управлению жизненным циклом отдельных задач и самого исполнителя
ScheduledExecutorService, расширяющий ExecutorService, поддерживает периодическое выполнение задач или их запуск в будущем
•Обычно переменные-ссылки на объекты исполнителей объявляются одного из этих трех интерфейсных типов без указания конкретного имени класса исполнителя
Все права защищены. www.haulmont.ru info@haulmont.com |
© HAULMONT, 2013 |

Интерфейс Executor
45
•Интерфейс Executor предоставляет единственный метод, execute(), разработанный для замены общего подхода создания потоков
•Если r является Runnable объектом и e является Executor объектом, вы можете заменить
(new Thread(r)).start();
на
e.execute(r);
•Однако определение execute() менее специфично:
Низкоуровневые подходы создают новый поток и немедленно запускают его
В зависимости от реализации Executor, execute() может сделать то же самое, но более вероятно использует существующий рабочий поток для запуска r или размещения r в очереди ожидания доступного рабочего потока
•Реализации исполнителей в java.util.concurrent спроектированы так, чтобы в полной мере использовать более продвинутые
ExecutorService и ScheduledExecutorService интерфейсы, хотя они также работают с базовым интерфейсом Executor
Все права защищены. www.haulmont.ru info@haulmont.com |
© HAULMONT, 2013 |

|
Интерфейс ExecutorService |
46 |
Интерфейс ExecutorService поддерживает выполнение |
• |
|
|
с похожим, но более универсальным методом submit() |
• |
Подобно execute(), submit() принимает Runnable |
|
объекты, а также Callable объекты, которые позволяют |
|
задаче возвращать значение |
• |
Метод submit() возвращает Future объект, который |
|
используется для получения возвращаемого значения |
|
Callable и имеет возможность отслеживать состояние |
|
Callable и Runnable задач |
• |
ExecutorService также содержит методы для запуска |
|
больших коллекций Callable объектов |
• |
Наконец, ExecutorService предоставляет ряд методов |
|
для управления остановкой исполнителя |
• |
Для поддержки немедленной остановки задачи должны |
|
корректно обрабатывать прерывания |
Все права защищены. www.haulmont.ru info@haulmont.com |
© HAULMONT, 2013 |

Интерфейс
ScheduledExecutorService
47
• Интерфейс ScheduledExecutorService
поддерживает методы своего родителя ExecutorService с возможностью составления графика, по которому выполняет Runnable или Callable задачу после заданной задержки
• В дополнение, интерфейс объявляет методы scheduleAtFixedRate() и scheduleWithFixedDelay(), которые исполняют указанные задачи периодически с определенным интервалом
Все права защищены. www.haulmont.ru info@haulmont.com |
© HAULMONT, 2013 |

Thread Pools
48
•Большинство реализаций исполнителей в java.util.concurrent используют пулы потоков, которые состоят из рабочих потоков
•Этот тип потоков существует отдельно от Runnable и Callable задач, которые он выполняет, и часто используется для выполнения нескольких задач
•Использование рабочих потоков сводит к минимуму накладные расходы на создание потоков
•Потоки используют значительный объем памяти, а в крупных приложениях выделение и освобождение ресурсов под множество объектов потоков создает значительные накладные расходы на управление памятью
•Один из распространенных типов пулов потоков – это фиксированный пул потоков:
У пула этого типа всегда есть определенное количество выполняющихся потоков
Если поток почему-то уничтожается, пока он еще используется, то он автоматически заменяется на новый поток
Задачи представляются в пул через внутреннюю очередь, в которую помещаются дополнительные задачи всякий раз, когда активных задач больше, чем потоков
Все права защищены. www.haulmont.ru info@haulmont.com |
© HAULMONT, 2013 |

Thread Pools
49
Важным преимуществом фиксированного пула потоков является то, что приложения, использующие этот подход, плавно снижают свою производительность. Чтобы понять это, рассмотрим веб-сервер, где каждый запрос HTTP обрабатывается в отдельном потоке. Если приложение просто создает новый поток для каждого нового запроса HTTP и система получает больше запросов, чем она может обработать немедленно, приложение может вдруг перестать отвечать на все запросы, когда накладные расходы на все эти потоки превысят возможности системы. С лимитом на количество потоков, которые могут быть созданы, приложение не будет обслуживать HTTP-запросы так быстро, как они приходят, но он будет обслуживать их настолько быстро, насколько система может выдержать.
Все права защищены. www.haulmont.ru info@haulmont.com |
© HAULMONT, 2013 |

Executors
50
•Простой способ создания исполнителя, который использует фиксированный пул потоков, заключается в вызове метода newFixedThreadPool() фабрики java.util.concurrent.Executors
•Этот класс также предоставляет следующие методы фабрики:
Метод newCachedThreadPool() создает исполнителя с расширяемым пулом потоков, этот исполнитель подходит для приложений, которые запускают множество коротких задач
Метод newSingleThreadExecutor() создает исполнителя, который выполняет одну задачу за один раз
Несколько фабричных методов версии ScheduledExecutorService для представленных выше исполнителей
•Если ни один из исполнителей, представленных вышеупомянутыми фабричными методами, не отвечает вашим требованиям, создание экземпляров java.util.concurrent.ThreadPoolExecutor или java.util.concurrent.ScheduledThreadPoolExecutor через конструкторы даст вам дополнительные опции
Все права защищены. www.haulmont.ru info@haulmont.com |
© HAULMONT, 2013 |