• Like
Expert Java Day: Java concurrency
Upcoming SlideShare
Loading in...5
×

Expert Java Day: Java concurrency

  • 3,026 views
Uploaded on

Expert Java Day, 3 декарбря, Одесса, …

Expert Java Day, 3 декарбря, Одесса,
Александр Кормушин — ведущий разработчик, Deutsche Bank, Москва

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • Slide 25 -
    final synchronized float get()
    Should be
    final synchronized int get()
    .And by the way - as i know - read instructions is not synchronized for volatile variables.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
3,026
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
48
Comments
1
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • Нефункциональные требованияПотребности для распарраллеливания вычислений:Увеличение производительности за счет равномерного использования нескольких процессоровОбработка больших объемов данных Эффективная загрузка процессора, если слабое место – данные, но работу с ними можно вести параллельно. Например работа с данными из разных испочников с данными которые можно обрабатывать параллельно и в конце получить цельный результатДолгие вычисленияСистема становится масштабируемой, если вычисления хорошо распараллеливаются, а данные позволяют параллельную обработкуOf the various aspects of performance, the "how much" aspects 􀍲 scalability, throughput, and capacity 􀍲 are usually of greater concern for server applications than the "how fast" aspects.Масштабируемость системы зависит от распараллеливаемостиAvoid premature optimization. First make it right, then make it fast 􀍲 if it is not already fast enough.
  • Может исходить из функциональных требований к системе, например:Система должна быть многопользовательскойМногозадачность (пользователь может запускать несколько задач одновременно в системе)Выполнение каких-либо действий в фоновом режиме (проигрывание фильмов, музыки)Выполнение каких-либо действий по расписанию
  • The J2EE design revolves around the request/response paradigm. For a login request, a user typically provides a user name and password to the server and waits for a response to get access to the site. A J2EE container can serve multiple users at the same time (in parallel) by managing a pool of threads, but for various reasons opening independent threads within a single J2EE container is not recommended. Some containers' security managers will not allow user programs to open threads. Moreover, if some containers allowed opening threads, then they would not manage those threads and therefore no container-managed services would be available for the threads. That would leave you to implement these services manually, which is tedious and liable to add complexity to your code.
  • Различия небольшиеПример использования:Нужно объединить данные из разных источников для отображения на одной странице. Например через сторонние вебсервисы.
  • WorkManager=ExecutorServiceWork=Runnable/CallableWorkItem=FutureWorkEvent=
  • Через консоль
  • Коммуникация между компонентами, использующими JMS, асинхронна (процедура не дожидается ответа на своё сообщение) и независима от исполнения компонентов.JMS - транспорт
  • Коммуникация между компонентами, использующими JMS, асинхронна (процедура не дожидается ответа на своё сообщение) и независима от исполнения компонентов.
  • @Singleton@Asynchronouspublic class AsyncBean { @Asynchronous //this line can be omittedpublic void ignoreResult(int a, int b) { // the client doesn't care what happens here } @Asynchronous //Аннотациpublic Future<Integer> longProcessing(int a, int b) {return new AsyncResult<Integer>(a * b); }}
  • Все клиенты вызывают методы одного экземпляраspec does not address "high-end" features like clustering, load-balancing and fail-over. However, I think this is a valuable comment to send to the EG comments email alias.As far as I know, major vendors do plan to implement clustering, load-balancing and fail-over for EJB Singletons, so you should have your wish :-).
  • One of the significant enhancements made in JSR 315: Java Servlet 3.0, is support for asynchronous processing. With this support, a servlet no longer has to wait for a response from a resource such as a database before its thread can continue processing, that is, the thread is not blocked. Previous to Servlet 3.0, asynchronous support in the Java EE web container was provided in a proprietary way — either through APIs built on top of the Servlet 2.5 API or through non-Servlet APIs or implementations.In modern web applications there are times when you need to asynchronously handle some part of the request, so that the web container can continue serving other requests in a seamless fashion. One example is a chat room application. In this type of application you want to enable long-lived client connections with a servlet. You don't want a server thread to be blocked for a long period of time serving a request from a single client. You want the servlet to process a request from the client and then free up the server thread as quickly as possible for other work. In other words, you want the request to be processed and a response generated asynchronously.With the upcoming release of the Servlet 3.0 specification, standard asynchronous support has been added to the Servlet API. This Tech Tip introduces the asynchronous support that has been added to the Servlet API in Java Servlet 3.0 technology. It also includes a sample application that demonstrates those features.

Transcript

  • 1. Приёмы параллельногопрограммирования в Java
  • 2. Виды параллельных вычисленийРаспараллеливание вычислений может быть на уровне: систем (распределенные системы, облачные вычисления, grid, cluster, etc.) процессов (процессы ОС) потоковВзаимодействие потоков выполнения может быть через: общую память общее хранилище данных (базу данных)Page  2
  • 3. Мотивация Увеличение производительности за счет равномерного использования нескольких процессоров или разнородных систем: – Обработка больших объемов данных (Построение отчетов, бизнес аналитика, индексирование больших хранилищ) – Долгие вычисления (Шифрование, переборные алгоритмы (расчет маршрутов, минимизация издержек, решение математических задач, игры и т.д.) Масштабируемость системы (если эффективно распараллеливается – можно задействовать больше машин)Page  3 3
  • 4. МотивацияМожет исходить из функциональных требований к системе, например:  Система должна быть многопользовательской  Многозадачность  Выполнение каких-либо действий в фоновом режиме или асинхронно  Выполнение каких-либо действий по расписанию  Отзывчивый пользовательский интерфейсPage  4 4
  • 5. Подходы к распараллеливанию вычислений Разделяемый доступ к общей памяти – блокировки, синхронизация критических секций, барьеры памяти, атомарные инструкции типа CAS, базы данных, транзакции Обмен сообщениями – Erlang (модель акторов), Occam (CSP) – потоки не имеют доступа к общей памяти, но могут обмениться сообщениями синхронно или асинхронно – Можно реализовать и в Java с использованием Immutable типов, глубокого копирования данных и обмена сообщениями (например JMS) Полная изолированность потоков вычислений (Одни потоки могут порождать другие при этом передавая на вход данные. Поток обрабатывает исходные данные и возвращает результат породившему потоку) – MapReduce (AppEngine Mapper API, Apache Hadoop), – J2EE WorkManagerPage  5
  • 6. Возникающие проблемы Разделяемый доступ к общей памяти – Сохранение целостности данных при одновременном доступе (thread-safety), • непредсказуемость фактического порядка исполнения инструкций (race conditions) – Конечность вычислений • взаимная блокировка потоков (deadlock) • зависание потока (starvation) • тупиковая ситуация (livelock) • инверсия приоритетов (priority inversion) – Низкая производительность • ожидание очереди на блокировку • переключение контекста (context switching)Page  6
  • 7. Плюсы и минусы подходов Обмен сообщениями – код всегда потокобезопасен – легче масштабировать – дополнительные затраты на коммуникации – сложнее строить алгоритмы Изолированные потоки – сложность алгоритмического распараллеливания обработки данных – легко масштабируется – нет всех вышеперечисленных проблемPage  7
  • 8. Взаимная блокировкаPage  8
  • 9. Граф блокировок потоков B А E D CPage  9
  • 10. Взаимная блокировка. final Object lockA = new Object(); final Object lockB = new Object(); new Thread() { public void run() { synchronized (lockA) { synchronized (lockB) { // Do something } } } }.start(); new Thread() { public void run() { synchronized (lockB) { synchronized (lockA) { // Do something } } } }.start();Page  10
  • 11. Дамп потоковFull thread dump Java HotSpot(TM) 64-Bit Server VM (20.1-b02 mixed mode):"Thread-1" prio=6 tid=0x0000000006998800 nid=0x1a48 waiting for monitor entry [0x00000000076af000] java.lang.Thread.State: BLOCKED (on object monitor) at DeadlockDemo$2.run(DeadlockDemo.java:27) - waiting to lock <0x00000007d5d0a6e8> (a java.lang.Object) - locked <0x00000007d5d0a6f8> (a java.lang.Object)"Thread-0" prio=6 tid=0x0000000006998000 nid=0x1620 waiting for monitor entry [0x000000000748f000] java.lang.Thread.State: BLOCKED (on object monitor) at DeadlockDemo$1.run(DeadlockDemo.java:16) - waiting to lock <0x00000007d5d0a6f8> (a java.lang.Object) - locked <0x00000007d5d0a6e8> (a java.lang.Object)…Page  11
  • 12. Вывод информации о блокировкахFound one Java-level deadlock:============================="Thread-1": waiting to lock monitor 0x000000000058ca80 (object 0x00000007d5d0a6e8, a java.lang.Object), which is held by "Thread-0""Thread-0": waiting to lock monitor 0x000000000058f330 (object 0x00000007d5d0a6f8, a java.lang.Object), which is held by "Thread-1"Java stack information for the threads listed above:==================================================="Thread-1": at DeadlockDemo$2.run(DeadlockDemo.java:27) - waiting to lock <0x00000007d5d0a6e8> (a java.lang.Object) - locked <0x00000007d5d0a6f8> (a java.lang.Object)"Thread-0": at DeadlockDemo$1.run(DeadlockDemo.java:16) - waiting to lock <0x00000007d5d0a6f8> (a java.lang.Object) - locked <0x00000007d5d0a6e8> (a java.lang.Object)Found 1 deadlock.Page  12
  • 13. Взаимная блокировка взаимодействующих объектовclass A { public synchronized void callB(B b){ b.process(); } public synchronized void process(){}}class B { public synchronized void callA(A a){ a.process(); } public synchronized void process(){}}final A a = new A();final B b = new B();//Thread1a.callB(b);//Thread 2b.callA(a);Page  13
  • 14. Дамп потоков"Thread-1" prio=6 tid=0x0000000006ab1000 nid=0x18a8 waiting for monitor entry [0x00000000076ef000] java.lang.Thread.State: BLOCKED (on object monitor) at DeadlockDemo1$A.process(DeadlockDemo1.java:16) - waiting to lock <0x00000007d5d0bac0> (a DeadlockDemo1$A) at DeadlockDemo1$B.callA(DeadlockDemo1.java:26) - locked <0x00000007d5d0cc30> (a DeadlockDemo1$B) at DeadlockDemo1$2.run(DeadlockDemo1.java:45)"Thread-0" prio=6 tid=0x0000000006ab0000 nid=0x17e0 waiting for monitor entry [0x00000000075ef000] java.lang.Thread.State: BLOCKED (on object monitor) at DeadlockDemo1$B.process(DeadlockDemo1.java:31) - waiting to lock <0x00000007d5d0cc30> (a DeadlockDemo1$B) at DeadlockDemo1$A.callB(DeadlockDemo1.java:11) - locked <0x00000007d5d0bac0> (a DeadlockDemo1$A) at DeadlockDemo1$1.run(DeadlockDemo1.java:39)Page  14
  • 15. Случай c аппаратом лучевой терапии Therac-25Этот аппарат работал в трёх режимах: Электронная терапия: электронная пушка напрямую облучает пациента; компьютер задаёт энергию электронов от 5 до 25 МэВ. Рентгеновская терапия: электронная пушка облучает вольфрамовую мишень, и пациент облучается рентгеновскими лучами, проходящими через конусообразный рассеиватель. В этом режиме энергия электронов одна: 25 МэВ. В третьем режиме никакого излучения не было. На пути электронов (на случай аварии) располагается стальной отражатель, а излучение имитируется светом. Этот режим применяется для того, чтобы точно навести пучок на больное место.Page  15
  • 16. Модель памяти Java. Зачем это нужно знать?public class NoVisibility { private static boolean ready; private static int number; private static class ReaderThread extends Thread { public void run() { while (!ready) Thread.yield(); System.out.println(number); } } public static void main(String[] args) { new ReaderThread().start(); number = 42; ready = true; }}Page  16
  • 17. Модель памяти JavaЧто это?Это часть спецификации языка Java (JLS chapter 17), описывающая нанизком уровне принципы хранения переменных в памяти (полейобъекта, класса, элементов массива и т.д.) и доступа потоков к общей памяти.Описывает поведение многопоточных программ в терминах: Атомарности операций Видимости данных Порядка выполнения инструкцийPage  17
  • 18. Модель памяти Java. Атомарность операцийКакие операции являются атомарными?Атомарные операции – эффект которых на систему семантически такой жекак если бы они выполнялись мгновенно и результат не зависел от работыдругих потоков.Согласно модели памяти Java: – Чтение и запись значений в память соответствующую переменной (кроме типов long и double) является атомарной операцией – Чтение и запись указателей атомарно вне зависимости от разрядности указателя (32 или 64 бита) – Чтение и запись volatile long или double переменных атомарноPage  18
  • 19. Модель памяти Java. Видимость данныхВидимость данных - это правила, описывающие при каких обстоятельствахизменение переменных в одном потоке становится видимым для другихпотоков.Изменение данных одним поток становится видим для других, если: Пишущий поток освобождает блокировку, которую тут же получает другой поток Для volatile поля все изменения сделанные в одном потоке сразу же видны другим потокам. При первом обращении к полю объекта поток увидит либо его значение по умолчанию, либо значение записанное каким-либо потоком.Page  19
  • 20. Вторая семантика synchronized//Thread 1x = 1;synchronized(this){ y = 2;} Все переменные записаны в память Все переменные читаются из памяти //Thread 2 synchronized(this){ a = x; } b = y;Page  20
  • 21. Модель памяти Java. Видимость данных После окончания работы конструктора все final поля становятся видимыми другим потокам. Все модификации сделанные в конструкторе видны в finalize() При запуске нового потока, все модификации текущего потока становятся видны При завершении потока, все модификации переменных становятся видимыми другим потокамPage  21
  • 22. Модель памяти Java.Изменение порядка выполнения инструкций Компилятором (может менять инструкции местами для оптимизации, если это не нарушает логики выполнения программы*) Процессором Процессором может быть изменен порядок записи переменных из кэша в оперативную память (может производится в порядке отличном от того как они изменялись программой)Основной принцип:Программа должна сохранять семантику последовательного иоднопоточного выполнения.Page  22
  • 23. Модель памяти Java. Работа над ошибками.До Java 5: Не гарантированности видимости final полей класса после завершения конструктора. – Immutable объекты не были потокобезопасными Можно было получить ошибку в подобном коде: String s1 = "string"; String s2 = s1.substring(2); if (s2 != s2) throw new AssertionError();Page  23
  • 24. Модель памяти Java. Работа над ошибками.Чтение и запись volatile переменных были упорядочены между собой, но неупорядочены относительно других операций.Следующий код не работал: Map configOptions; char[] configText; volatile boolean initialized = false; . . . // In thread A configOptions = new HashMap(); configText = readConfigFile(fileName); processConfigOptions(configText, configOptions); initialized = true; . . . // In thread B while (!initialized) sleep(); // use configOptionsPage  24
  • 25. Volatile переменныеvolatile int value;Семантически эквивалентноprivate int value;final synchronized void set(int value) { this.value = value;}final synchronized float get() { return value;}Page  25
  • 26. Volatile переменныеИспользовать volatile можно, если: Доступ к полю происходит из разных потоков, но вне других блоков синхронизации (необходимых для других нужд) Поле не входит ни в какие инварианты для состояния объекта Модификация поля не зависит от его текущего значения В поле могут быть записаны только семантически корректные значенияНапример, один поток может модифицировать поле, а другие только читать.Page  26
  • 27. Volatile массивыРассмотрим массив:volatile int [] arr = new int[SIZE];arr = arr;int x = arr[0];arr[0] = 1;Сама ссылка на массив – volatile, но элементы массива - нет. Это жеприменимо и к коллекциям ArrayList, LinkedList и т.д.Есть атомарные реализации массивов с volatile элементами:AtomicIntegerArray, AtomicLongArray и AtomicReferenceArray<T>Page  27
  • 28. Безопасная публикация объектовДо завершения конструктора объект может быть не целостным (не все поляинициализированы, не все инварианты состояния выполняются)Источник неприятностей ссылка на объект доступна другому потоку до создания объекта и нет никаких специальных синхронизацийPage  28
  • 29. Пример небезобасной инициализацииpublic Holder holder;public void initialize() { holder = new Holder(42);} //Thread1... initialize();public class Holder { //Thread2 private int n; if (holder != null){ holder.assertSanity(); } public Holder(int n) { this.n = n; } public void assertSanity() { if (n != n) throw new AssertionError("This statement is false."); }}Page  29
  • 30. Антипаттерн – публикация this в конструктореpublic class ThisEscape {... public ThisEscape(EventSource source) { source.registerListener(new EventListener() { public void onEvent(Event e) { doSomething(e); } }); ... }}Page  30
  • 31. Решение – использовать фабричный методpublic class SafeListener { private final EventListener listener; private SafeListener() { listener = new EventListener() { public void onEvent(Event e) { doSomething(e); } }; } public static SafeListener newInstance(EventSource source) { SafeListener safe = new SafeListener(); source.registerListener(safe.listener); return safe; }}Page  31
  • 32. Принципы потокобезопасной инициализации Инициализировать ссылку на объект в статической инициализации Делать объект доступным другим потокам через ссылку, которая: – volatile – AtomicReference – final поле потокобезопасно инициализированного объекта – поле, доступ к которому синхронизированPage  32
  • 33. Неизменяемые (immutable) объекты- Объекты, состояние которых не изменяется в течение жизни объектаНапример: String, Integer, Double и т.д.Объект эффективно считается неизменяемым, если: его состояние не может быть изменено после создания все поля определяющие состояние – final* объект инициализирован потокобезопасно (ссылка на него недоступна другим потокам до завершения конструктора)Immutable объекты всегда потокобезопасны.Page  33
  • 34. Пример immutable классаpublic class ImmutablePoint { private final double x; private final double y; public ImmutablePoint(double x, double y) { this.x = x; this.y = y; }}Page  34
  • 35. Реализация потокобезопасной инициализации синглтонаЗадача: есть синглтон. Надо реализовать его отложенную потокобезопасную инициализацию.public class Singleton { private static Singleton instance; public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } private Singleton(){ ... }}Page  35
  • 36. Антипаттерн – Блокировка с двойной проверкой (DCL)public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { // 1 if (instance == null) { // 2 instance = new Singleton(); // 3 } } } return instance;}Page  36
  • 37. Вариант исправленияpublic static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { // 1 Singleton inst = instance; // 2 if (inst == null) { synchronized (Singleton.class) { // 3 inst = new Singleton(); // 4 } instance = inst; // 5 } } } return instance;}Page  37
  • 38. Потокобезопасное решение начиная с Java 5Так как поменялась семантика volatile переменных, можно определитьinstance так:private static volatile Singleton instance;тогда в строкеinstance = new Singleton();не может произойти перестановки операций вызова конструктора иприсвоения значения переменной.Но все же использовать не рекомендуется.Page  38
  • 39. Корректная реализация отложенной инициализациисинглтонаpublic class Something { private static class LazySomethingHolder { public static Something something = new Something(); } private Something(){ ... } public static Something getInstance() { return LazySomethingHolder.something; } ...}Page  39
  • 40. Базовые элементы аппаратной синхронизацииПомимо синхронизации для потокобезопасного доступа к разделяемымипеременным можно использовать атомарные инструкция вида сравнение-собменом (Compare-and-Swap, CAS):boolean compareAndSwap(reference, expectedValue, newValue)Изменение значения переменной происходит, только если текущее значениесовпадает с ожидаемым. В противном случае ничего не происходитCAS поддерживается на аппаратном уровне всеми современнымипроцессорами.Page  40
  • 41. Сравнение с обменом (CAS)CAS семантически эквивалентна следующему кодуpublic class SimulatedCAS { private int value; public synchronized int getValue() { return value; } public synchronized boolean compareAndSwap(int expectedValue, int newValue) { if (value == expectedValue) { value = newValue; return true; } return false; }}Page  41
  • 42. Атомарные типы данныхНачиная с Java 5 появились реализацииAtomicInteger, AtomicBoolean, AtomicReference и др.(java.util.concurrent.atomic.*), которые предоставляют атомарныеоперации основанные на CAS.Атомарные типы ведут себя так же, как volatile переменные.Пример методов класса AtomicInteger: int addAndGet(int i, int delta) int decrementAndGet() int getAndDecrement() int getAndIncrement()Page  42
  • 43. Реализация потокобезопасного счётчикаpublic static Counter counter = new Counter();public class Counter { private long value = 0; public synchronized long getValue() { return value; } public synchronized long increment() { return ++value; } //Thread1} print(counter.getValue()); //Thread2 counter.increment();Page  43
  • 44. Реализация счётчика с помощью AtomicLongpublic class Counter { private AtomicLong value = new AtomicLong(); private volatile long value; public long getValue() { return value.get(); public final long get() { return value; } } public long increment() { return value.incrementAndGet(); } public final long incrementAndGet() {} for (;;) { long current = get(); long next = current + 1; if (compareAndSet(current, next)) return next; } }Page  44
  • 45. Неблокирующие алгоритмыПотокобезопасные алгоритмы, использующие для доступа к общим даннымCAS, называются неблокирующими.Основной принцип:некоторый шаг алгоритма выполняется гипотетически, с осознанием того, чтоон должен быть выполнен повторно, если операция CAS закончиласьнеудачно.Неблокирующие алгоритмы часто называют оптимистическими, потому чтоони выполняются с предположением о том, что не будет конфликтныхситуацийPage  45
  • 46. Потокобезопасный стек. Алгоритм Трайбера import java.util.concurrent.atomic.AtomicReference; public class ConcurrentStack<E> { AtomicReference<Node<E>> head = new AtomicReference<Node<E>>(); static class Node<E> { final E item; Node<E> next; public Node(E item) { this.item = item; } } public void push(E item) { Node<E> newHead = new Node<E>(item); Node<E> oldHead; do { oldHead = head.get(); newHead.next = oldHead; } while (!head.compareAndSet(oldHead, newHead)); }Page  46
  • 47. Потокобезопасный стек. Алгоритм Трайбера. public E pop() { Node<E> oldHead; Node<E> newHead; do { oldHead = head.get(); if (oldHead == null) return null; newHead = oldHead.next; } while (!head.compareAndSet(oldHead, newHead)); return oldHead.item; }}Page  47
  • 48. Неблокирующая очередь. Алгоритм Майкла-Скотаclass LinkedQueue<E> implements Queue<E> { private static class Node<E> { E item; final AtomicReference<Node<E>> next; Node(E item, Node<E> next) { this.item = item; this.next = new AtomicReference<Node<E>>(next); } } private AtomicReference<Node<E>> head = new AtomicReference<Node<E>>( new Node<E>(null, null)); private AtomicReference<Node<E>> tail = head; Page  48
  • 49. Неблокирующая очередь. Добавление элемента.public boolean offer(E item) { Node<E> newNode = new Node<E>(item, null); for (;;) { Node<E> curTail = tail.get(); Node<E> residue = curTail.next.get(); if (curTail == tail.get()) { if (residue == null) { if (curTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(curTail, newNode); return true; } } else { tail.compareAndSet(curTail, residue); } } }}Page  49
  • 50. Неблокирующая очередь. Получение первого элемента.public E poll() { for (;;) { Node<E> curHead = head.get(); Node<E> curTail = tail.get(); Node<E> newHead = curHead.next.get(); if (curHead == head.get()) { if (curHead == curTail) { if (newHead == null) { return null; } else { tail.compareAndSet(curTail, newHead); } } else if (head.compareAndSet(curHead, newHead)) { E item = newHead.item; if (item != null) { newHead.item = null; return item; } } } }Page  50}
  • 51. Неблокирующие коллекции в JavaС Java 5: – ConcurrentHashMap<K,V> (Использует CAS + гранулярные блокировки для модификации) – ConcurrentLinkedQueue<E>С Java 6: – ConcurrentSkipListMap<K,V> – ConcurrentSkipListSet<E>C Java 7: – ConcurrentLinkedDeque<E>Плюс: нет взаимных блокировок и ошибок одновременной модификацииМинусы: ниже производительность при интенсивной модификации.Операция size() линейная по времени.Page  51
  • 52. Копирование при модификацииДругой подход – копировать данные при изменении коллекции.Реализации: – CopyOnWriteArrayList<E> – CopyOnWriteArraySet<E>Плюсы: – нет блокировок при чтении – iterator() работает со своей копией коллекции на момент вызова метода – Операции записи синхронизированы и делают копию массива с даннымиМинусы: – экстенсивное потребление памятиPage  52
  • 53. Список с пропусками Поиск, вставка, удаление – O(log n)Page  53
  • 54. Fork/JoinFork/Join - фреймворк для многопоточного программирования в Java 7основанный на легковесных потоках (задачах).Есть бэкпорт фреймворка для Java 6.Основные классы: ForkJoinPool – управляет распределением задач по потока, управляет запуском, аналог ExecutorServer ForkJoinTask/RecursiveTask/RecursiveAction – аналоги Thread/Callable/RunnablePage  54
  • 55. Fork/Join на примереinterface TreeNode { Iterable<TreeNode> getChildren(); //Retrieve values sequentially by net from some service long getValue();}public class MaxCounter extends RecursiveTask<Long>{ private final TreeNode root; public MaxCounter(TreeNode root) { this.root = root; } public static long maxValue(TreeNode root){ return new ForkJoinPool(100).invoke(new MaxCounter(root)); }Page  55
  • 56. Fork/Join на примере @Override protected Long compute() { List<MaxCounter> subTasks = new ArrayList<>(); for(TreeNode child : root.getChildren()) { MaxCounter task = new MaxCounter(child); task.fork(); subTasks.add(task); } long max = 0; for(MaxCounter task : subTasks) { long result = task.join(); if (result > max) max = result; } return max; }}Page  56
  • 57. Fork/Join – управление блокировкамиclass ManagedLocker implements ManagedBlocker { final ReentrantLock lock; boolean hasLock = false; ManagedLocker(ReentrantLock lock) { this.lock = lock; } public boolean block() { if (!hasLock) lock.lock(); return true; } public boolean isReleasable() { return hasLock || (hasLock = lock.tryLock()); }}…ForkJoinPool.managedBlock(new ManagedLocker(lock));Page  57
  • 58. Многопоточное программирование в J2EEEJB контейнер берет на себя управление системными ресурсами:безопасность, управление потоками, управление ресурсами (pooling,управление транзакциями), позволяет масштабировать приложение.Вследствие этого возникают множество ограничений на использование API вприложениях.Enterprise bean’ы не должны:1. управлять потоками выполнения2. использовать средства синхронизации для разграничения доступа к ресурсам между экземплярами EJB3. работать с файловой системой4. использовать reflection, native вызовыИ т.д.Page  58 58
  • 59. Средства параллельного программирования в J2EE WorkManager API (JSR-237) Messaging (JMS, MDB) Асинхронный вызов EJB (@Asynchronous в EJB 3.1) Вызов EJB по расписанию (Timer Service, @Timeout, @Schedule в EJB 3.1) Синхронизация методов в EJB (@Lock в EJB 3.1) Асинхронный вызов веб сервисов (JAX-WS) Асинхронный ответ в сервлете (Servlet 3.0)Page  59 59
  • 60. WorkManager APIJSR-237 - API для параллельного выполнения задач в среде J2EE.Реализации: В Oracle Weblogic и IBM WebShpere используется реализация JSR-237 CommonJ В JBoss – WorkManager API из JCA (JSR-322)Page  60 60
  • 61. WorkManager APIPage  61 61
  • 62. Использование WorkManager’а Реализовать интерфейс Work. Фактические действия содержатся здесь Реализовать интерфейс WorkListener для получения результата выполнения Настроить WorkManager в сервере приложений (параметры pool’а потоков, сервер или кластер для выполнения и др.) Добавить ресурс в ejb-jar.xml или web.xmlPage  62 62
  • 63. Реализация интерфейса Work import commonj.work.Work; public class WorkImpl implements Work { @Override public void run() { // Собственно сами действия } @Override public void release() { // WorkManager собирается остановить выполнение } @Override public boolean isDaemon() { // true если задача требует много времени на выполнение return false; } }Page  63 63
  • 64. Реализация интерфейса WorkListener import commonj.work.WorkEvent; import commonj.work.WorkListener; public class WorkListenerImpl implements WorkListener { @Override public void workAccepted(WorkEvent workEvent) { } @Override public void workCompleted(WorkEvent workEvent) { } @Override public void workRejected(WorkEvent workEvent) { } @Override public void workStarted(WorkEvent workEvent) { } }Page  64 64
  • 65. Пример создания задачи //#1. Получить WorkManager из JNDI WorkManager workManager; InitialContext ctx = new InitialContext(); this.workManager = (WorkManager)ctx.lookup("java:comp/env/wm/TestWorkManager"); //#2. Создать Work and WorkListener Work work = new WorkImpl("HelloWorld"); WorkListener workListener=new WorkListenerImpl(); //#3. Назначить выполнение задачи WorkItem workItem = workManager.schedule(work, workListener); List<WorkItem> workItemList=new ArrayList<WorkItem>(); workItemList.add(workItem); //#4. Дождаться выполнения всех задач this.workManager.waitForAll(workItemList, WorkManager.INDEFINITE); //#5. Получить результаты выполнения, если требуется WorkImpl workImpl= (WorkImpl)workItem.getResult();Page  65 65
  • 66. Контекст выполнения задач Для POJO задач  Задача выполняется с тем же контекстом безопасности  Java:comp вызывающей компоненты доступен в задаче  Можно использовать UserTransaction  Можно использовать удаленное выполнение Для EJB задач  Это обязательно должна быть @Local ссылка на @Stateless session bean  Задача выполняется с тем же контекстом безопасности и он может быть переопределен в EJB  Можно использовать декларативные транзакции  Используется java:comp enterprise bean’аPage  66 66
  • 67. Удаленное выполнение задачJSR-237 WorkManager может выполнять задачи на удалённых JVM: Work объект должен реализовывать Serializable Эта функциональность зависит от сервера приложений Реализовано в CommonJPage  67 67
  • 68. Java Messaging SystemJava Message Service (JMS) — стандарт middleware длярассылки сообщений, позволяющий приложениям, выполненным наплатформе J2EE, создавать, посылать, получать и читать сообщения.Может использоваться как транспорт для вызова внутренних компонет удаленного вызова вызова SOAP веб сервисовМожно использовать как синхронно, так и асинхронноPage  68 68
  • 69. Java Messaging SystemПреимущества JMS: Надежность Гарантированность доставки (поддержка транзакций) Масштабируемость АсинхронностьPage  69 69
  • 70. Отправка JMS сообщения @Resource(mappedName="jms/ConnectionFactory") private static ConnectionFactory connectionFactory; @Resource(mappedName="jms/Queue") private static Queue queue; connection = connectionFactory.createConnection(); session = connection.createSession(true, Session.SESSION_TRANSACTED); messageProducer = session.createProducer(queue); message = session.createTextMessage(); for (int i = 0; i < NUM_MSGS; i++) { message.setText("This is message " + (i + 1)); System.out.println("Sending message: " + message.getText()); messageProducer.send(message); }Page  71 71
  • 71. Получение JMS сообщения @MessageDriven(mappedName="jms/Queue", activationConfig = { @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue") }) public class SimpleMessageBean implements MessageListener { public void onMessage(Message inMessage) { TextMessage msg = null; try { if (inMessage instanceof TextMessage) { msg = (TextMessage) inMessage; logger.info("MESSAGE BEAN: Message received: " + msg.getText()); } else { logger.warning("Message of wrong type: " + inMessage.getClass().getName()); } } catch (JMSException e) { e.printStackTrace(); mdc.setRollbackOnly(); } catch (Throwable te) { te.printStackTrace(); } } }Page  72 72
  • 72. Асинхронный вызов EJBВ EJB 3.1 появилась простая возможность делать асинхронные вызовы:добавить @Asynchronous аннотацию к методу или всему классу.При вызове такого метода клиент не будет дожидаться окончаниявыполнения.Page  73 73
  • 73. Асинхронный вызов EJB Существует два вида асинхронных методов: •Метод возвращает void. •Метод возвращает Future<?> @Stateless //@Asynchronous public class AsyncBean { @Asynchronous public void ignoreResult(int a, int b) { // … } @Asynchronous public Future<Integer> longProcessing(int a, int b) { return new AsyncResult<Integer>(a * b); } }Page  74 74
  • 74. EJB Timer ServicesEJB контейнер предоставляет возможность запускать методы по рассписанию Можно запускать методы по расписанию, через определенное время, через определенные интервалы Можно создавать программно, аннотациями или в deployment конфигурации Таймеры можно создавать в @Stateless, @Singleton и @MessageDriven bean’ах Таймеры persistent по умолчаниюPage  75 75
  • 75. Пример таймера @Singleton @Startup public class DummyTimer3 { @Resource TimerService timerService; @PostConstruct public void initTimer() { if (timerService.getTimers() != null) { for (Timer timer : timerService.getTimers()) { if (timer.getInfo().equals("dummyTimer3.1") || timer.getInfo().equals("dummyTimer3.2")) timer.cancel(); } } timerService.createCalendarTimer( new ScheduleExpression(). hour("*").minute("*").second("*/10"), new TimerConfig("dummyTimer3.1", true)); timerService.createCalendarTimer( new ScheduleExpression(). hour("*").minute("*").second("*/45"), new TimerConfig("dummyTimer3.2", true)); } @Timeout public void timeout(Timer timer) { System.out.println(getClass().getName() + ": " + new Date()); } }Page  76 76
  • 76. Пример таймера EJB 3.1 @Schedules({ @Schedule(hour="*", minute="*", second="*/10", info="every tenth"), @Schedule(hour="*", minute="*", second="*/45", info="every 45th") }) public void printTime(Timer timer) { System.out.println(getClass().getName() + ": " + new Date() + ":" + timer.getInfo()); }Page  77 77
  • 77. Синхронизация методов в EJBНовые возможности в EJB 3.1: @Singleton – enterprise bean, который реализован как singleton session bean. Существует только один экземпляр bean’а. Можно использовать межпоточную синхронизацию.Для @Singleton bean’ов можно указывать порядок создания: @Startup – bean инициализируется при запуске приложения @DependsOn(“MyBean1”, “MyBean2”) – указанные bean’ы должны быть инициализированы доPage  78 78
  • 78. Синхронизация управляемая контейнером @Singleton @AccessTimeout(value=120000) public class BufferSingletonBean { private StringBuffer buffer; @Lock(READ) private String getContent(){ return buffer.toString(); } @Lock(WRITE) public void append(String string) { buffer.append(string); } @Lock(WRITE) @AccessTimeout(value=360000) public void flush { //Save to db ... buffer = new StringBuffer(); } }Page  79 79
  • 79. Программная синхронизация @ConcurrencyManagement(BEAN) @Singleton public class BufferSingletonBean { private StringBuffer buffer; private ReadWriteLock lock = new ReentrantReadWriteLock(); private String getContent(){ try{ lock.readLock().lock(); return buffer.toString(); }finally{ lock.readLock().unlock(); } } public void append(String string) { try{ lock.writeLock().lock(); buffer.append(string); }finally{ lock.writeLock().unlock(); } } public void flush { try{ lock.writeLock().lock(); //Save to db ... buffer = new StringBuffer(); }finally{ lock.writeLock().unlock(); } } }Page  80 80
  • 80. Асинхронный вызов веб сервисов Начиная с JAX_WS 2.0 асинхронный вызов веб сервиса можно просто реализовать на стороне клиента. Клиентский прокси будет сам управлять асинхронным вызовом. Для этого надо: 1. Добавить пользовательский binding 2. Сгенерировать асинхронный клиентский код 3. Вызывать веб сервис через асинхронные методыPage  81 81
  • 81. Генерация клиентского кода для асинхронного вызоваУказать пользовательский binding при вызове wsimport<bindings node="wsdl:definitions"> <packagename="com.company.jaxws.stockquote.client"/> <enableAsyncMapping>true</enableAsyncMapping></bindings>Ant задача:<wsimport debug="${debug}" verbose="${verbose}" keep="${keep}"extension="${extension}" destdir="${build.classes.home}" wsdl="${client.wsdl}"><binding dir="${basedir}/etc" includes="${client.binding.async}" /></wsimport>Page  82 82
  • 82. Клиентский код @WebService(name = "StockQuote") public interface StockQuote { … public Response<GetQuoteResponse>getQuoteAsync( @WebParam(name = "arg0", targetNamespace = "") String arg0); … public Future<?>getQuoteAsync(String arg0, AsyncHandler<GetQuoteResponse>asyncHandler); … public double getQuote( String arg0); }Page  83 83
  • 83. Вызов веб сервиса //#1 Response<GetQuoteResponse>resp = port.getQuoteAsync (code); while( ! resp.isDone()){ //some client side processes } GetQuoteResponse output = resp.get(); //#2 port.getQuoteAsync ("MHP", new GetQuoteCallbackHandler (){ private GetQuoteResponse output; public void handleResponse(Response<GetQuoteResponse>response) { try { output = response.get (); } catch (ExecutionException e) { e.printStackTrace (); } catch (InterruptedException e) { e.printStackTrace (); } } );Page  84 84
  • 84. Асинхронный ответ в сервлете @WebServlet("/foo" asyncSupported=true) public class MyServlet extends HttpServlet { public void doGet(HttpServletRequest req, HttpServletResponse res) { ... AsyncContext aCtx = request.startAsync(req, res); ScheduledThreadPoolExecutor executor = new ThreadPoolExecutor(10); executor.execute(new AsyncWebService(aCtx)); } } public class AsyncWebService implements Runnable { AsyncContext ctx; public AsyncWebService(AsyncContext ctx) { this.ctx = ctx; } public void run() { // Какие-либо действия ctx.dispatch("/render.jsp"); } }Page  85 85
  • 85. Полезная литература по теме Concurrency in Practice, Brian Goetz, Doug Lea, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes Pattern-Oriented Software Architecture, Patterns for Concurrent and Networked Objects, F. Buschmann, K.Henney, D. Schmidt, M. Stal, H. Rohnert Patterns in java, Mark Grand Concurrent Programming in Java, Design Principles and Patterns, Doug Lea Patterns for Parallel Programming, T.Mattson, B.Sanders, B. MassingillPage  86 86
  • 86. Интернет ресурсы Leveraging EJB Timers for J2EE Concurrency http://www.devx.com/Java/Article/33694/1954 The Work Manager API: Parallel Processing Within a J2EE Container http://www.devx.com/Java/Article/28815/1954 Simple Asynchronous methods in EJB 3.1 http://javahowto.blogspot.com/2010/03/simple-asynchronous- methods-in-ejb-31.html Java Memory Model http://gee.cs.oswego.edu/dl/cpj/jmm.htmlPage  87 87
  • 87. Интернет ресурсы Double-checked locking http://www.ibm.com/developerworks/java/library/j- dcl/index.html Non-blocking algorithms http://www.ibm.com/developerworks/java/library/j-jtp04186/ Fork/Join framework http://www.oracle.com/technetwork/articles/java/fork-join- 422606.html http://www.ibm.com/developerworks/java/library/j- jtp03048/index.htmlPage  88 88
  • 88. Ваши вопросы?Page  89 89
  • 89. Спасибо за внимание!Page  90 90