Saint Petersburg, 2012 Java Lecture #05 Concurrency.

Презентация:



Advertisements
Похожие презентации
Usage java.util.concurrence in Java For students of universities Author: Oxana Dudnik.
Advertisements

Коллекции классов Лекция 12. С помощью коллекций вместо создания структур данных программист использует готовые структуры данных, не заботясь об их реализации.
§68 Лучше executors and tasks, чем потоки. Executor framework - 1.5, java.util.concurrent –выключить (.shutdown() ) –ждать пока не выполнится один или.
Java Collections Framework Commons-collections Коллекции в многопоточной среде Коллекции в Java.
Многопоточное программирование на Java Java Advanced.
Java : массивы и коллекции. Массивы Массивы простых типов: int []a = new int[10]; int []b = new int[]{ 0, 1, 2, 3, 4, 5 }; Массивы ссылочных типов (reference.
Параллелизм и потоки в Java For students of university Author: Oxana Dudnik.
Многопоточное программирование на Java Java Advanced
Ресурсы WPF Два типа ресурсов WPF: объектные ресурсы (object resource) – определенный.NET-объект, который можно использовать многократно; ресурсы сборки.
Многопоточное программирование на Java Java Advanced.
Библиотека стандартных шаблонов (STL) ( Standard Template Library) набор согласованных обобщённых алгоритмов, контейнеров, средств доступа к их содержимому.
Исполнение программы Энциклопедия учителя информатики Газета «Первое сентября»
OpenGL и Direct3D сравнение стандартов Выполнил: Пенкин А. Группа И-204.
6. Средства синхронизации и взаимодействия процессов 6.1. Проблема синхронизации Процессам Процессам часто нужно взаимодействовать друг с другом, например,
ТЕОРИЯ И ПРАКТИКА МНОГОПОТОЧНОГО ПРОГРАММИРОВАНИЯ Тема 15 Методика разработки и анализа неблокирующих алгоритмов. Д.ф.-м.н., профессор А.Г. Тормасов Базовая.
Параллельное программирование с использованием технологии OpenMP Аксёнов Сергей Владимирович к.т.н., доцент каф.ОСУ ТПУ Лекция 3 Томский политехнический.
Основные виды ресурсов и возможности их разделения.
Что такое связи между таблицами В реляционной базе данных связи позволяют избежать избыточности данных. Например, в ходе создания базы данных, содержащей.
Полиморфизм Полиморфизм (polymorphism) - последний из трех "китов", на которых держится объектно-ориентированное программирование Слово это можно перевести.
СУБД Microsoft Access 2003 Элементы языка SQL. Язык SQL SQL (Structured Query Language) – структурированный язык запросов Язык SQL применяется во многих.
Транксрипт:

Saint Petersburg, 2012 Java Lecture #05 Concurrency

Agenda Примитивы синхронизации Thread-safe коллекции Планировщики и пулы потоков Fork/Join Framework Утилитные классы

Lock – интерфейс, обозначающий мьютекс в явном виде При этом гораздо более гибкий, чем стандартные java-мониторы Основные отличия от synchronized-блоков Вы сами создаете объект-мьютекс Вы сами решаете какие ресурсы защищать Вы сами ответственны за освобождение мьютекса Можно захватывать мьютекс в одном контексте, а отпускать в другом Реализация синхронизации на Lockах во многих случаях эффективнее synchronized-блоков Из за большей гибкости она позволяет накладывать более слабые условия на взаимодействия потоков Lockи тяжелее отлаживать и диагностировать проблемы, ведь с точки зрения JVM это рядовые объекты. Для synchronized-блокировок всегда можно запросить у JVM Thread dump, который покажет все потоки и взятые ими synchronized-блокировки. Lock

Lock Рассмотрим реализацию thread-safe счетчика с использованием Lock В отличие от synchronized Lock является не средством языка, а обычным объектом с набором методов В этом случае критическую секцию ограничивают операции lock() и unlock() Вызов lock() блокирует, если Lock в данный момент занят, поэтому удобно использовать метод tryLock(), который сразу вернет управление и результат При использовании Lock не будут работать стандартные методы wait(), notify() и notifyAll(), ведь монитор как таковой не используется Вместо них используются реализации интерфейса Condition, ассоциированные с Lock: необходимо вызвать Lock.newCondition() и уже у Condition вызывать методы await(), signal() и signalAll() С одним Lock можно ассоциировать несколько Condition

Lock Наиболее распространенный паттерн для работы с Lockами представлен справа Он гарантирует, что Lock будет отпущен в любом случае, даже если при работе с ресурсом будет выброшено исключение Для synchronized этот подход неактуален – там средствами языка предоставляется гарантия, что мьютекс будет отпущен Этот паттерн весьма полезен в любой ситуации, требующей обязательного освобождения ресурсов Широко используются две основные реализации Lock: ReentrantLock допускает вложенные критические секции ReadWriteLock имеет разные механизмы блокировки на чтение и запись, позволяя уменьшить накладные расходы

Lock fairness Fairness (равнодоступность) – свойство Locka, при котором при освобождении управление отдается тому из ожидающих потоков, который ждет дольше всех Fairness не распространяется на действия собственно планировщика потоков Fair Locks менее производительны, но более предсказуемы, чем Unfair В действительности равнодоступность блокировок - очень сильное требование и достигается за счет значительных потерь в производительности Учет использования системных ресурсов и синхронизация, необходимые для обеспечения равнодоступности означают, что соперничающие равнодоступные блокировки будут иметь гораздо более низкую пропускную способность, чем неравнодоступные По умолчанию следует установить для равнодоступности значение false, если для правильности вашего алгоритма не критично, чтобы потоки обслуживались точно в порядке очереди. Блокировки на synchronized изначально unfair и нет способа изменить это поведение

Lock – масштабируемость Тест для измерения относительной масштабируемости synchronized в сравнении с Lock, использует генератор псевдослучайных чисел (PRNG). Диаграммы показывают пропускную способность в вызовах в секунду, нормализованную до случая synchronized с одним потоком для различных реализаций. Как видно, реализация основанная на Lock гораздо лучше масштабируется Тест наглядно показывает, что Fair Lock – достаточно дорогое удовольствие

Как обеспечить atomicity и visibility без memory barriera? Compare-and-set (compare-and-swap, CAS) – инструкция, поддерживаемая на уровне процессора (lock:cmpxchg) Она позволяет сравнить значение с содержимым памяти и при совпадении выполнить запись Эта инструкция позволяет применять оптимистичные блокировки без переключения контекста потока при занятом ресурсе Все Atomic-обертки содержат метод compareAndSet(…) Принцип работы CAS в псевдокоде: CAS на многопроцессорных машинах будет дороже из-за аппаратной реализации атомарности операции Compare-And-Set

Atomic wrappers 9 Обертки над примитивными типами AtomicInteger AtomicLong AtomicBoolean AtomicReference Предоставляют реализации CAS-операций Содержат набор полезных атомарных операций boolean compareAndSet(int expect, int update) int incrementAndGet() int getAndIncrement() int getAndSet(int newValue) int addAndGet(int delta) boolean weakCompareAndSet(int expect, int update)

Atomic wrappers - пример

Agenda Примитивы синхронизации Thread-safe коллекции Планировщики и пулы потоков Fork/Join Framework Утилитные классы

Collections.synchronized…() Класс Collections содержит среди прочих методы Collections.synchronizedCollection(Collection c) Collections.synchronizedList(List list) Collections.synchronizedMap(Map m) Collections.synchronizedSet(Set s) Они возвращают обертки над коллекциями-аргументами с синхронизированными методами Этими методами очень удобно оборачивать уже существующие коллекции Содержимое можно небезопасно менять путем модификации коллекции-источника Итерирование требует внешней синхронизации на коллекции Не очень хорошо масштабируются

Legacy implementations HashTable – синхронизированная реализация интерфейса Map Все методы синхронизированы Потребляет заметно меньше памяти, чем ConcurrentHashMap Плохо масштабируется Последовательности операций на HashTable могут нуждаться в дополнительной внешней синхронизации, если требуется атомарность Vector – синхронизированная реализация интерфейса List Все методы синхронизированы Не осуществляет копирования при записи Не дает значительного overheadа по памяти Процесс итерирования требует внешней синхронизации на самой коллекции

Java.util.concurrent – новые интерфейсы

java.util.ConcurrentMap Любые попытки сделать реализацию Map thread-safe упираются в необходимость атомарности группы операций Например: «Если в Map нет такого ключа, то положить его» Требует двух операций, которые должны выполняться атомарно Для достижения атомарности придется самостоятельно писать внешние средства синхронизации Непонятно как увязать их с синхронизацией самой коллекции ConcurrentMap добавляет к Map методы для обработки часто встречающихся связанных операций на Map: V putIfAbsent(K key, V value) что эквивалентно

java.util.ConcurrentMap boolean remove(Object key, Object value) boolean replace(K key, V oldValue, V newValue) V replace(K key, V value)

ConcurrentHashMap Основная thread-safe реализация интерфейса Map Реализует также ConcurrentMap Внутри похожа на HashMap, но имеет дополнительные механизмы синхронизации Масштабируется гораздо лучше HashTable, практически линейно Не синхронизирует операции чтения Операции чтения отражают результат последней завершенной операции записи, не учитывая те, что еще в процессе Итераторы отображают состояние коллекции на момент создания итератора Позволяет задавать concurrency level – размер сегмента хэш- таблицы, блокируемого на запись Потребляет заметно больше памяти, чем HashTable

ConcurrentHashMap Версии реализации от 7 и ниже используют сегментированную структуру При записи блокируется не весь Map, а один сегмент Разрабатываемая версия 8 будет блокироваться уже на конкретных bucketах, а сегменты исчезнут

ConcurrentHashMap vs HashTable 19 HashTable блокирует всю таблицу целиком, ограничивая вертикальную масштабируемость С какого-то момента добавление новых ядер уже не дает прироста производительности

Blocking Queues 20 Отлично подходят для реализации шаблона Producer-Consumer Добавляют набор блокирующих методов для работы с очередью Могут быть Fair по отношению к использующим потокам ArrayBlockingQueue Ограниченная очередь на базе массива PriorityBlockingQueue Очередь с сортировкой элементов по Comparatorу Неограниченная очередь SynchronousQueue Очередь из одного(!) элемента Операция добавления блокирует до соответствующей операции чтения из другого потока Интерфейс BlockingDeueue расширяет BlockingQueue методами работы с обоими концами структуры

Blocking queues: API reference

Copy-on-write 22 CopyOnWriteArrayList и CopyOnWriteArraySet основаны на массиве, копируемом при операции записи Уже открытые итераторы при этом не увидят изменений в коллекции Эти коллекции следует использовать только когда 90+% операций являются операциями чтения При частых операциях модификации большая коллекция способна убить производительность Сортировка этих коллекций не поддерживается, т.к. она подразумевает O(n) операций вставки Итераторы по этим коллекциям не поддерживают операций модификации

Copy-on-write - Реализация И её реализация в CopyOnWriteArrayList: Операция добавления элемента в список:

Skip Lists ConcurrentSkipListMap и ConcurrentSkipListSet основаны на Skip Listах Это единственные доступные thread-safe реализации NavigableSet и NavigableMap Skip List, как правило, занимает больше памяти, чем хэш-таблица Гарантирует O(log(n)) для большинства операций ConcurrentSkipListMap, в отличие от ConcurrentHashMap, не предоставляет средств для performance-тюнинга ConcurrentSkipListMap также реализует ConcurrentMap Это единственные упорядоченные thread-safe коллекции

Итераторы Как правило итераторы коллекций из java.util.concurrent не бросают ConcurrentModificationException Они не являются fail-fast Они гарантированно отражают состояние коллекции на момент создания итератора Итераторы не блокируют другие операции или итераторы на исходной коллекции При этом они могут содержать и более поздние изменения, но это не гарантируется

Итераторы Выполнение этого кода приводит к ConcurrentModificationException Если заменить реализацию на CopyOnWriteArrayList, то исключения не будет

Agenda Примитивы синхронизации Thread-safe коллекции Планировщики и пулы потоков Fork/Join Framework Утилитные классы

Callable 28 Имеет единственный метод V call() По принципу действия схож с Runnable

Executor Executor – интерфейс, обозначающий абстрактную систему для асинхронного исполнения задач В него передают исполняемый код, а он заботится о выборе потока для исполнения При этом он может содержать несколько потоков, обеспечивая их эффективное переиспользование Класс Executors представляет собой фабрику для создания Executorов Эта фабрика позволяет создавать разнообразные очереди и пулы потоков, избавляя программиста от необходимости писать однообразный инфраструктурный код Простой пример использования Executorа представлен ниже

ExecutorService Представляет собой расширение Executorа с дополнительными возможностями Способен создавать объекты Future, представляющие собой результаты выполнения асинхронных операций Основные методы: Submit(…) – различные варианты этого метода принимают задачу на выполнение invokeAll()- метод выполнит переданный в него список задач и вернет управление тогда, когда все задачи будут завершены или наступит таймаут invokeAny()- метод выполнит переданный в него список задач и вернет управление тогда, когда хотя бы одна задача будет завершена или наступит таймаут shutdown() – при вызове этого метода ExecutorService закончит выполнение текущих задач, но новых принимать уже не будет Многие Executorы, возвращаемые фабрикой Executors на самом деле являются реализациями ExecutorService

Future Future – интерфейс, семантически обозначающий результат выполнения асинхронной операции Тип-параметр – это тип результата операции Важные методы интерфейса get() позволяет получить результат операции, блокируя, если результата еще нет cancel() останавливает выполнение задачи, если только она еще не завершена isDone() позволяет определить, завершена ли задача Все операции в рамках выполнения задачи happens-before любых операций после вызова метода get() Самой распространенной реализацией Future является FutureTask FutureTask также реализует Runnable, так что его экземпляры удобно передавать в Thread или Executor

Future - Пример

Agenda Примитивы синхронизации Thread-safe коллекции Планировщики и пулы потоков Fork/Join Framework Утилитные классы

Fork/Join – это подход к написанию многопоточных программ, основанный на следующем рекурсивном алгоритме: Если задача достаточно мала – выполнить её Если нет – разбить на несколько и выполнять их, а результаты агрегировать Для подзадач вернуться к пункту 1. Этот подход отлично работает для большого количества однотипных задач Схема справа представляет собой типовое дерево задач, полученное в результате декомпозиции Fork/Join

Fork/Join Framework Начиная с Java 7 в стандартные библиотеки Java включен Fork/Join Framework, который предоставляет инфраструктуру для подобной декомпозиции Изначально он входил в JSR-166, который описывал практически все содержимое пакета java.util.concurrent Тем не менее, ему потребовалось еще 6 лет, чтобы попасть в мейнстрим При этом существует много сторонних реализаций Fork/Join, например Tymeac

Fork/Join Framework API Интерфейс ForkJoinTask представляет собой небольшую задачу как результат декомпозиции на этапе Fork. Есть две реализации: RecursiveAction – не возвращает результат работы для этапа Join RecursiveTask - возвращает результата типа T для использования на этапе Join ForkJoinPool – реализация Executorа для ForkJoinTask В конструкторе ему можно задать размер пула, по умолчанию он равен количеству процессоров в системе С каждым потоком связана очередь задач, пополняемая вызовами fork() Поток исполняет их, начиная с самых новых Если очередь заканчивается, то поток старается украсть задачи из очередей других потоков пула

Fork/Join Framework - Пример Пример вычисляет N-ый член последовательности Фибоначи методом Fork/Join Одна часть выполняется в текущем потоке, вторая – шедулится на Thread pool

Agenda Примитивы синхронизации Thread-safe коллекции Планировщики и пулы потоков Fork/Join Framework Утилитные классы

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

Semaphore Объект, позволяющий войти в заданный участок кода не более чем n потокам одновременно N определяется параметром конструктора При N=1 по действию аналогичен Lock Fairness – гарантия очередности потоков 40

CountDownLatch 41 Он предоставляет две основные операции: countDown() – уменьшает значение счетчика на единицу await() – текущий поток будет заблокирован пока значение счетчика не упадет до нуля Перезапустить CountDownLatch нельзя Утилитный класс для синхронизации действий потоков Представляет собой барьер, на котором потоки ждут некоторого события В основе лежит счетчик, который можно уменьшать от начального значения до нуля

CountDownLatch - Пример

CyclicBarrier Позволяет N потокам дождаться друг друга в некоторой точке выполнения N задается параметром конструктора Как только все N потоков вызовут await() их разом отпустит Опционально можно задать некий Runnable, который будет выполнен в момент разблокировки CyclicBarrier можно перезапустить методом reset()

CyclicBarrier - Пример 44

Library Brian Goetz. Java concurrency in practice Java Language Specification, глава 17 Maurice Herlihy, Nir Shavit. The art of multiprocessor programming Статьи Brianа Goetzа на (например 45