Java весна 2014 лекция 5

50
Углубленное программирование на Java Лекция 5 «Многопоточность» Виталий Чибриков

description

 

Transcript of Java весна 2014 лекция 5

Page 1: Java весна 2014 лекция 5

Углубленное программирование на

Java Лекция 5

«Многопоточность»

Виталий Чибриков

Page 2: Java весна 2014 лекция 5

План лекции

2

1. Thread, Runnable=2. Frontend и

Account Service=3. Concurrent

Collections=4. Message System

Page 3: Java весна 2014 лекция 5

Processes and Threads

3

ПроцессыПриложение со своим набором run-time ресурсов и собственной памятью

Взаимодействие через Inter Process Communication ресурсы

Можно запускать на нескольких компьютерах

Потоки«Живут» в одном процессе

Старт приложения – создание main потока

Используют общую память (heap) и другие ресурсы приложения

Потоки могут порождать другие потоки и взаимодействовать с ними

Page 4: Java весна 2014 лекция 5

Что такое поток?

4

ПотокОбъект, у класса которого есть методы start() и run()

После вызова метода start() будет выполнен run()

Метод run() будет выполнен в своем стеке

Page 5: Java весна 2014 лекция 5

Threads

5

main

T1.run()

T2.run()

T1.start()

T2.start()

t

Page 6: Java весна 2014 лекция 5

Порядок не определен

6

main

T1.run()T2.run()

T1.start()

T2.start()

t

Page 7: Java весна 2014 лекция 5

Роль операционной системы

7

Операционная системаСоздает потоки

Переключает потоки

Следит за мониторами

Page 8: Java весна 2014 лекция 5

interface Runnable

8

Всего один метод – run()

RunnableПоток это объект, реазизующий интерфейс Runnable

public class HelloRunnable implements Runnable { G public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } }

Page 9: Java весна 2014 лекция 5

class Thread

9

class MyThread extends Thread

Thread содержит метод start() ― запуск нового потока

сlass Thread реализует интерфейс Runnable

public class HelloThread extends Thread { public void run() { System.out.println("Hello from a thread!"); } G public static void main(String args[]) { (new HelloThread()).start(); } }

Page 10: Java весна 2014 лекция 5

Runnable

Runnable vs Thread

10

Runnable класс нужно передавать в конструктор Thread объекта

Можно наследовать класс отличный от Thread

ThreadСодержит методы управления потоком

Thread thread = Thread.currentThread();Текущий Thread object можно получить в любом месте кода

Page 11: Java весна 2014 лекция 5

Доступ к объекту потока

Текущий Thread object можно получить в любом месте кода

long getId() String getName() int getPriority() void setPriority(int priority) static void sleep(long ms) void interrupt() static boolean interrupted() void join()

11

Thread thread = Thread.currentThread();

Некоторые методы

Page 12: Java весна 2014 лекция 5

sleep and interrupt

Если нужно остановить выполнение потокаThread.sleep(1000) – остановит выполнение потока на 1 секунду

Если нужно прервать выполнение потокаthread.interrupt() – пошлет прерывание потоку thread try { Thread.sleep(5000); } catch (InterruptedException e) { // We've been interrupted. return; } Gfor (int i = 0; i < inputs.length; i++) { heavyTask(inputs[i]); if (Thread.interrupted()) { // We've been interrupted. return; } } 12

Page 13: Java весна 2014 лекция 5

Если надо остановить текущий поток до окончания другого потока

join

13

Текущий поток ждет пока завершиться поток threadВ текущем потоке вызываем thread.join().

public class HelloThread extends Thread { public void run() { System.out.println(“1. Hello from a thread!"); } public static void main(String args[]) { Thread thread = new HelloThread(); thread.start(); thread.join(); System.out.println(“2. Hello from the main!"); } }

Page 14: Java весна 2014 лекция 5

Взаимодействие потоков

У потоков общий Heap

Можно передать в два потока ссылку на один объект

Потоки смогут менять общий объект и взаимодействовать через него

14

Как осуществить взаимодействие между потоками?

Page 15: Java весна 2014 лекция 5

Java memory model

Memory model + volatile

15

volatile – не кэшировать, всегда считывать из общей памяти

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

Кэширование значений в многопроцессорных средах

Изменение порядка операций для оптимизации

Основные проблемы

final – не изменять значение переменной

synchronized – отметить участок кода доступный только одному треду

Инструменты для решения

Page 16: Java весна 2014 лекция 5

Synchronization

Демонстрация работы кода ThreadInterference.example();

Возможные ошибки одновременного доступа

Thread Interference – потеря результатаMemory Consistency Errors – ошибочное состояние общей памяти

16

Page 17: Java весна 2014 лекция 5

Synchronization

public synchronized void increment() { ++i; }

public void addName(String name) { synchronized(lockObject) { lastName = name; nameCount++; } nameList.add(name); }

17

Synchronized methods

Synchronized statements

Page 18: Java весна 2014 лекция 5

Lock object (mutex)

public class TwoLocks { private long c1 = 0; private long c2 = 0; private Object lock1 = new Object(); //the first lock private Object lock2 = new Object(); //the second lock public void c1Up() { synchronized(lock1) { c1++; } } G public void c2Up() { synchronized(lock2) { c2++; } } }

18

Page 19: Java весна 2014 лекция 5

Deadlock

public void c1c2Up() { synchronized(lock1) { c1++; synchronized(lock2) { c2++; } } } G public void c2c1Up() { synchronized(lock2) { c2++; synchronized(lock1) { c1++; } } }

19

Page 20: Java весна 2014 лекция 5

Служба в отдельном потоке

20

private boolean needDoSomething; Gpublic void run() { while(true){ if(needDoSomething){ doSomething(); } Thread.sleep(1000); } }

Page 21: Java весна 2014 лекция 5

wait() and notify()

wait(), notify() и notifyAll() ― методы класса Object

object.wait() ― ждать в текущем потоке, пока не придет notify()

object.notify() ― сигнал «продолжить» первому кто начал wait()

object.notifyAll() ― сигнал «продолжить» всем кто начал wait()

Демонстрация работы кода RandomRunExample.example();

21

Page 22: Java весна 2014 лекция 5

План лекции

22

1. Thread, Runnable=2. Frontend и

Account Service=3. Concurrent

Collections=4. Message System

Page 23: Java весна 2014 лекция 5

UserSession

23

UserSession содержит:

Каждому, кто пришел на сервер - UserSession

Каждой UserSession – sessionId (из HttpSession)

String sessionId

String userName

Long userId

На Frontend-е Map<String, UserSession> sessionIdToSession;

Page 24: Java весна 2014 лекция 5

В одном потоке

24

Назначаем Id для пользовательской сесcии

В методе handle() спрашиваем у AccountService userId по имени

Создаем объект Accounter, который будет скрывать авторизацию

Ждем пока AccountService прочитает эти данные из базы

Создаем на основе сесcии страницу и отдаем ее браузеру

Сохраняем в объекте сессии данные о пользователе

Спрашиваем у пользователя имя

Авторизация

Page 25: Java весна 2014 лекция 5

Временная диаграмма

Авторизация

25

browser server

start on localhost:8080http://localhost:8080/generate sessionId

first access

enter your name

submit name

load userId

Hello user!

Page 26: Java весна 2014 лекция 5

Frontend и Account Service

26

Frontend создает пользовательскую сессиюG

Frontend возвращает страницу созданную на основе сесcии в браузерG

Frontend запрашивает у Account Service данные по авторизацииG

Когда данные приходят, Frontend меняет состояние сессии

Разведем работу с пользователем и AccountService по разным потокамG

Frontend ― поток который работает с пользователямиG

AccountService ― поток который работает с авторизацией

Page 27: Java весна 2014 лекция 5

Состояния

27

Ответ 2: sessionId + «Ждите авторизации»

Запрос 0: Первый запрос страницы.

Запрос 2: sessionId

Запрос 3: sessionId

Ответ 3: sessionId + «Ваше имя» + userName + « ваш Id: » + userId

Ответ 0: sessionId + «Введите имя»

Запрос 1: sessionId + ИмяОтвет 1: sessionId + «Ждите авторизации»

Запрос на AccountServer

Ответ не пришел

Ответ пришел

Page 28: Java весна 2014 лекция 5

Frontend и Account Service

Решение в 2 потока

28

frontend account service

get Id from Storage

get Id by name

return Id for name

Hello user!

submit name

wait for auth

check state

wait for auth

check state

Page 29: Java весна 2014 лекция 5

План лекции

29

1. Thread, Runnable=2. Frontend и

Account Service=3. Concurrent

Collections=4. Message System

Page 30: Java весна 2014 лекция 5

Atomic

30

java.util.concurrent.atomicAtomicBoolean

AtomicInteger

AtomicLong

Реализованы без использования synchronized

public final int incrementAndGet() { while (true) { int current = get(); //get() возвращает текущее значение (volatile) int next = current + 1; if (compareAndSet(current, next)) return next; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }

optimistic locking

Page 31: Java весна 2014 лекция 5

Synchronized vs. Concurrent

31

Concurrent ― предназначена для работы с несколькими потоками,но не синхронная (без использования synchronized)G

ConcurrentHashMap ― concurrent

java.util.Hashtable ― synchronized

synchronized ― гарантия, что только один поток работает с элементом

Concurrent ― разрешено одновременное чтение и безопасная запись

Page 32: Java весна 2014 лекция 5

Concurrent Collections

32

CopyOnWriteArrayListG G копирование при вставке в ArrayList

CopyOnWriteArraySetG G Set интерфейс над CopyOnWriteArrayList

ConcurrentHashMapG G thread safe HashMap

ConcurrentSkipListMapG G ключи уникальны и отсортированы

ConcurrentSkipListSetG G set на базе ConcurrentSkipListMap

Контейнеры безопасные для многопоточного доступа

Page 33: Java весна 2014 лекция 5

Очереди безопасные для многопоточного доступа

Concurrent Queues

33

BlockingQueueG G G G очередь с ограничениме размера

ConcurrentLinkedQueueG G G thread safe очередь

LinkedBlockingQueueArrayBlockingQueue

BlockingDequeG G G G двухсторонняя «очередь»ArrayBlockingDeque

Page 34: Java весна 2014 лекция 5

План лекции

34

1. Thread, Runnable=2. Frontend и

Account Service=3. Concurrent

Collections=4. Message System

Page 35: Java весна 2014 лекция 5

Thread-local объекты

Основная идея

Обмен сообщениями

35

Один поток кладет сообщение в коллекцию

Второй поток достает сообщение и исполняет его

Thread-Safe коллекцииБезопасная работа с элементами коллекцииОптимальная работа

Объекты на которые есть ссылки только из одного потока

Page 36: Java весна 2014 лекция 5

MessageSystem ― объект для обмена данными

Message System

36

Одна система сообщений на процесс

Единственный объект доступный из нескольких потоков

По одной очереди сообщений на поток

Каждый поток берет свою очередь из потока и выполняет сообщенияКаждый поток имеет свой адрес

Из любого места потока можно положить сообщение в очередь по адресу

Page 37: Java весна 2014 лекция 5

Обмен сообщениями

37

Frontend Account GService

Account GQueue

Frontend GQueue

MsgToAccountService MsgToFrontend

MessageSystem

Page 38: Java весна 2014 лекция 5

Address и Abonent

38

public class Address { static private AtomicInteger abonentIdCreator = new AtomicInteger(); final private int abonentId; G public Address(){ this.abonentId = abonentIdCreator.incrementAndGet(); } G public int hashCode() { return abonentId; } }

public interface Abonent { Address getAddress(); }

Page 39: Java весна 2014 лекция 5

Message

39

public abstract class Msg { final private Address from; final private Address to; G public Msg(Address from, Address to){ this.from = from; this.to = to; } G protected Address getFrom(){ return from; } G protected Address getTo(){ return to; } G public abstract void exec(Abonent abonent); }

Page 40: Java весна 2014 лекция 5

Message to Account Service

40

public abstract class MsgToAS extends Msg{ G public MsgToAS(Address from, Address to) { super(from, to); } G void exec(Abonent abonent) { if(abonent instanceof AccountService){ exec((AccountService) abonent); } } G abstract void exec(AccountService accountService); }

Page 41: Java весна 2014 лекция 5

Message to Account Service

41

public class MsgGetUserId extends MsgToAS { private String name; private String sessionId; public MsgGetUserId(Address from, Address to, String name, String sessionId) { super(from, to); this.name= name; this.sessionId = sessionId; } G void exec(AccountService accountService) { Long id = accountService.getUserId(name); Msg back = new MsgUpdateUserId(getTo(), getFrom(), sessionId, id); accountService.getMessageSystem(). sendMessage(back); } }

Page 42: Java весна 2014 лекция 5

Иерархия сообщений

42

Msg

MsgToAS MsgToFrontend

MsgUpdateUserIdMsgGetUserId

- Address from- Address to

- String name- Integer sessionId

- Integer sessionId- Integer userId

Page 43: Java весна 2014 лекция 5

Message System

43

private Map<Address, ConcurrentLinkedQueue<Msg>> messages = new HashMap<Address, ConcurrentLinkedQueue<Msg>>(); GGpublic void sendMessage(Msg message){ Queue<Msg> messageQueue = messages.get(message.getTo()); messageQueue.add(message); } Gpublic void execForAbonent(Abonent abonent) { Queue<Msg> messageQueue = messages.get(abonent.getAddress()); while(!messageQueue.isEmpty()){ Msg message = messageQueue.poll(); message.exec(abonent); } }

Page 44: Java весна 2014 лекция 5

MessageSystem ничего не знает о Frontend и AccountService

Все что нужно MessageSystem это Address, Abonent и Msg

Можно добавлять дополнительные сервисы

Абстракция

44

Page 45: Java весна 2014 лекция 5

Address Service

45

Часть Message System которая знает адреса абонентов

Может вернуть адрес Account сервиса и Frontend

Производит балансировку, если сервисов несколько

Page 46: Java весна 2014 лекция 5

Address Service

46

public class AddressService { private Address accountService; G public Address getAccountService() { return accountService; } G public void setAccountService(Address accountService) { this.accountService = accountService; } }

AddressService можно хранить в MessageSystemGGAddress аккаунт сервера для пользователя можно хранить в UserSession

Page 47: Java весна 2014 лекция 5

Обмен сообщениями

47

frontend account service

get Id from Storage

MsgGetUserId

MsgUpdateUserId

Hello user!

submit name

wait for auth

check state

wait for auth

check state

Page 48: Java весна 2014 лекция 5

Метод run()

48

public void run() { while (true) { messageSystem.execForAbonent(this); Thread.sleep(TICK_TIME); } }

Page 49: Java весна 2014 лекция 5

Демонстрация кода

49

Переключаемся на код и смотрим как он работает

Page 50: Java весна 2014 лекция 5

Спасибо за внимание

Виталий Чибриков [email protected]