**Многопоточность** - это: - одновременное выполнение нескольких действий (например, отрисовка пользовательского интерфейса и передача файлов по сети) - ускорение вычислений (при наличии нескольких вычислительных ядер) **Закон Амдала**: $S(N)=\frac{1}{(1-P)+\frac{P}{N}}$, где: - $S$ - ускорение (speedup) - $P$ - доля вычислений, которые возможно распараллелить - $N$ - количество вычислительных ядер Доступ к разделяемому ресурсу: - Блокировка (mutual exclusion) - Неблокирующий доступ: - Lock-free - гарантированный прогресс приложения - Wait-free - гарантированный прогресс каждого потока (каждый поток завершает выполнение любой операции за конечное число шагов) Потоки представлены экземплярами класса `java.lang.Thread`: - `String getName()` - `Long getId()` - `boolean isDaemon()` - `StackTraceElement[] getStackTrace()` - `ThreadGroup getThreadGroup()` #### Создание потоков Создание потока подкласса `Thread`: ```java Thread thread = new Thread() { @Override public void run() { //... } } ``` ```java import.java.util.*; public class Main { public static void main(String[] args) { Thread th1 = new Thread() { public void run() { System.out.println(getName()); } }; Thread th2 = new Thread() { public void run() { System.out.println(getName()); } }; th1.start(); th2.start(); System.out.println("Main thread"); } } ``` ```java import.java.util.*; public class Main { private static class MyThread implements Runnable { public void run() { System.out.println(Thread.currentThread().getName()); } } public static void main(String[] args) { for (int i = 1; i <= 10; i++) new Thread(new myThread()).start(); th1.start(); th2.start(); System.out.println("Main thread"); } } ``` #### Управление потоками Управление потоками класса `java.lang.Thread`: - `start()` - `sleep()` - `join()` - `interrupt()` - `stop()` ```java import.java.util.*; public class Main { private static class MyThread implements Runnable { public void run() { try { //Thread.sleep(1000); System.out.println(Thread.currentThread()); } catch (InterruptedExceprion ex) {} } } public static void main(String[] args) { for (int i = 1; i <= 10; i++) { Thread th = new Thread(new myThread()); th.start(); th.join(); } System.out.println("Main thread"); } } ``` #### Жизненный цикл потока - **Создание объекта** `Thread` - **Запуск** - `thread.start()` - **Работа** - выполняется метод `run()`, `thread.isAlive() == true` - **Завершение** - метод `run()` закончился или бросил исключение - **Завершенный поток нельзя перезапустить** ## Синхронизация - **Взаимное исключение** - пока один поток что-то делает, другие не могут ему помешать - **Ожидание и уведомление** - поток ожидает уведомлений от других потоков #### Взаимное исключение Ключевое слово `synchronized`: - Синхронизированный метод: ```java public synchronized void doSomething() { //... } ``` - Синхронизированный блок внутри метода: ```java public void doSomething() { synchronized(obj) { //... } } ``` ```java public class Main { private static class MyThread1 implements Egor eg; Thread1(Egor _eg) {eg = _eg} public void run() { for (int i = 1; i < 100000; i+) eg.put(1) } public static void main(String[] args) thread { Egor eg = new Egor(10000); MyThread1 th1 = new MyThread1(eg); MyThread2 th2 = new MyThread2(eg); th1.start(); th2.start(); th1.join(); th2.join(); } } ``` - Синхронизация блоков - по монитору указанного объекта - Синхронизация методов - по монитору текущего объекта (`this`) - Синхронизация статических методов - по монитору класса #### Ожидание и уведомление Допустимо только внутри `synchronized` - `void wait()` - `void wait(long millis)` - `void wait(long millis, int nanos)` - `void notify()` - `void notifyAll()` ## Средства стандартных библиотек. Пакеты `java.util.concurrent` - Атомные типы - Примитивы синхронизации - Коллекции - Executors - Parallel Streams #### Атомные типы. Пакет `java.util.concurrent.atomic` - `AtomicBoolean` - `AtomicInteger` - `AtomicLong` - `AtomicReference` Операции: - `V get()` - `void set(V newValue)` - `boolean compareAndSet(V expect, V update)` Пример `AtomicInteger`: ```java AtomicInteger ai = new AtomicInteger(1); ai.getAndIncrement(); System.out.println(ai.get()); ai.addAndGet(100); System.out.println(ai.get()); ``` #### Семафоры. Класс `java.util.concurrent.semaphore` В отличие от `synchronized`-блока, одновременно могут работать несколько потоков (но не более заданного `N`). Операции: - `void acquire()` - `void release()` ```java Semaphore semaphore = new Semaphore(10); semaphore.acquire(); try { ///... } finally { semaphore.release(); } ``` #### Класс `java.util.concurrent.CountDownLatch` Обеспечивает точку синхронизации между `N` потоками (несколько потоков могут дожидаться друг друга и потом стартовать одновременно). Операции: - `void countDown()` - `void await()` ```java CountDownLach latch = new CountDownLatch(10); ``` ```java private static class MyThread1 implements { CountDownLatch latch; MyThread1(CountDownLatch _latch) { latch = _latch; new Thread(this).start(); } public void run() { for (int i = 1; i <= 5; i++) { System.out.println(i); latch.countDown(); } } } public static void main(String[] args) throw Exception { CountDownLatch cdl = new CountDownLatch(10); System.out.println('Потоки исполнения:'); new MyThread1(cdl); new MyThread1(cdl); try { cdl.await(); } catch (InterruptedException ex) { System.out.println(ex.toString()); } System.out.println("Основной поток завершен") } ``` #### Класс `java.util.concurrent.CyclicBarrier` Вариант `CountDownLatch`, допускающий повторное ожидание. #### Класс `java.util.concurrent.locks.ReentrantLock` Обеспечивает взаимное исключение потоков, аналогичное `synchronized`-блокам. Операции: - `lock()` - `unlock()` ```java Lock lock = new ReentrantLock(); lock.lock(); try { doSomething(); } finally { lock.unlock(); } ``` #### Класс `java.util.concurrent.locks.Condition` - Аналог `wait`/`notify` - Привязан к `Lock`'у - У одного `Lock`'а может быть много `Condition`'ов ```java Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); lock.lock(); try { while (!conditionSatisfied()) { condition.await(); } } finally { lock.unlock(); } condition.signal(); ``` ## Многопоточные варианты стандартных коллекций - `ConcurrentHashMap` - `ConcurrentSkipListMap` - `ConsurrnetSkipListSet` - `CopyOnWriteArrayList` - `CopyOnWriteArraySet` - и др. Более эффективны, чем полностью синхронизированные коллекции `java.util.Collections.synchronizedCollection()` ## Интерфейс `Executor` В многопоточный пакет `concurrent` для управления потоками включено средство, называемое сервисом исполнения `ExecutorService`. Данное средство служит альтернативой классу `Thread`, предназначенному для управления потоками. В основу сервиса исполнения положен интерфейс `Executor`, в котором определен один метод. ```java void execute(Runnable thread) ``` #### Класс `java.util.concurrent.ExecutorService` Инкапсулирует создание потоков, организацию очереди задач, распределение задач по потокам. Операции: - `Future submit(Runnable task)` - `Future submit(Callable task)` - `void shutdown()` - `List shutdownNow()` #### Класс `java.util.concurrent.Executors` фабрики класса - `ExecutorService newSingleThreadExecutor()` - `ExecutorService newFixedThreadPool(int nThreads)` - `ExecutorService newCachedThreadPool()` ```java private static class Main { public void run() { try { System.out.println(getName()); sleep(2000); } catch(InterruptedException ex) { //... } } } public static void main(String[] args) throw Exception { ExecutorService es = Executors.newFixedThreadPool(3); es.execute(new MyThread1()); es.execute(new MyThread1()); es.execute(new MyThread1()); es.execute(new MyThread1()); es.execute(new MyThread1()); es.shutdown(); System.out.println("Основной поток завершен") } ``` #### Класс `java.util.concurrent.ForkJoinPool` **Модель `fork-join`** - это метод, в котором мы разделяем каждую задачу (`fork`), а затем ждем объединения (`join`) всех получившихся подзадач и получаем результат. Принимает на исполнение `ForkJoinTask` ## Параллельные потоки - `stream.parallel()` - Возвращает `stream`, дальнейшие операции в котором будут исполняться параллельно ```java public static void main(String[] args) throw Exception { List list = Arrays.asList("Anna", "Egor", "Temka", "Leha", "Marsik"); list.stream().filter(p->p.length() == 4).forEach(System.out::println()); System.out.println("-----------------"); list.parallelStream().filter(p->p.length() == 4).forEach(System.out::println()); System.out.println("Основной поток завершен") } ```