Java весна 2014 лекция 5
-
Upload
technopark -
Category
Documents
-
view
136 -
download
0
description
Transcript of Java весна 2014 лекция 5
Углубленное программирование на
Java Лекция 5
«Многопоточность»
Виталий Чибриков
План лекции
2
1. Thread, Runnable=2. Frontend и
Account Service=3. Concurrent
Collections=4. Message System
Processes and Threads
3
ПроцессыПриложение со своим набором run-time ресурсов и собственной памятью
Взаимодействие через Inter Process Communication ресурсы
Можно запускать на нескольких компьютерах
Потоки«Живут» в одном процессе
Старт приложения – создание main потока
Используют общую память (heap) и другие ресурсы приложения
Потоки могут порождать другие потоки и взаимодействовать с ними
Что такое поток?
4
ПотокОбъект, у класса которого есть методы start() и run()
После вызова метода start() будет выполнен run()
Метод run() будет выполнен в своем стеке
Threads
5
main
T1.run()
T2.run()
T1.start()
T2.start()
t
Порядок не определен
6
main
T1.run()T2.run()
T1.start()
T2.start()
t
Роль операционной системы
7
Операционная системаСоздает потоки
Переключает потоки
Следит за мониторами
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(); } }
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(); } }
Runnable
Runnable vs Thread
10
Runnable класс нужно передавать в конструктор Thread объекта
Можно наследовать класс отличный от Thread
ThreadСодержит методы управления потоком
Thread thread = Thread.currentThread();Текущий Thread object можно получить в любом месте кода
Доступ к объекту потока
Текущий 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();
Некоторые методы
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
Если надо остановить текущий поток до окончания другого потока
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!"); } }
Взаимодействие потоков
У потоков общий Heap
Можно передать в два потока ссылку на один объект
Потоки смогут менять общий объект и взаимодействовать через него
14
Как осуществить взаимодействие между потоками?
Java memory model
Memory model + volatile
15
volatile – не кэшировать, всегда считывать из общей памяти
Описывает то, как потоки должны взаимодействовать через общую память
Кэширование значений в многопроцессорных средах
Изменение порядка операций для оптимизации
Основные проблемы
final – не изменять значение переменной
synchronized – отметить участок кода доступный только одному треду
Инструменты для решения
Synchronization
Демонстрация работы кода ThreadInterference.example();
Возможные ошибки одновременного доступа
Thread Interference – потеря результатаMemory Consistency Errors – ошибочное состояние общей памяти
16
Synchronization
public synchronized void increment() { ++i; }
public void addName(String name) { synchronized(lockObject) { lastName = name; nameCount++; } nameList.add(name); }
17
Synchronized methods
Synchronized statements
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
Deadlock
public void c1c2Up() { synchronized(lock1) { c1++; synchronized(lock2) { c2++; } } } G public void c2c1Up() { synchronized(lock2) { c2++; synchronized(lock1) { c1++; } } }
19
Служба в отдельном потоке
20
private boolean needDoSomething; Gpublic void run() { while(true){ if(needDoSomething){ doSomething(); } Thread.sleep(1000); } }
wait() and notify()
wait(), notify() и notifyAll() ― методы класса Object
object.wait() ― ждать в текущем потоке, пока не придет notify()
object.notify() ― сигнал «продолжить» первому кто начал wait()
object.notifyAll() ― сигнал «продолжить» всем кто начал wait()
Демонстрация работы кода RandomRunExample.example();
21
План лекции
22
1. Thread, Runnable=2. Frontend и
Account Service=3. Concurrent
Collections=4. Message System
UserSession
23
UserSession содержит:
Каждому, кто пришел на сервер - UserSession
Каждой UserSession – sessionId (из HttpSession)
String sessionId
String userName
Long userId
На Frontend-е Map<String, UserSession> sessionIdToSession;
В одном потоке
24
Назначаем Id для пользовательской сесcии
В методе handle() спрашиваем у AccountService userId по имени
Создаем объект Accounter, который будет скрывать авторизацию
Ждем пока AccountService прочитает эти данные из базы
Создаем на основе сесcии страницу и отдаем ее браузеру
Сохраняем в объекте сессии данные о пользователе
Спрашиваем у пользователя имя
Авторизация
Временная диаграмма
Авторизация
25
browser server
start on localhost:8080http://localhost:8080/generate sessionId
first access
enter your name
submit name
load userId
Hello user!
Frontend и Account Service
26
Frontend создает пользовательскую сессиюG
Frontend возвращает страницу созданную на основе сесcии в браузерG
Frontend запрашивает у Account Service данные по авторизацииG
Когда данные приходят, Frontend меняет состояние сессии
Разведем работу с пользователем и AccountService по разным потокамG
Frontend ― поток который работает с пользователямиG
AccountService ― поток который работает с авторизацией
Состояния
27
Ответ 2: sessionId + «Ждите авторизации»
Запрос 0: Первый запрос страницы.
Запрос 2: sessionId
Запрос 3: sessionId
Ответ 3: sessionId + «Ваше имя» + userName + « ваш Id: » + userId
Ответ 0: sessionId + «Введите имя»
Запрос 1: sessionId + ИмяОтвет 1: sessionId + «Ждите авторизации»
Запрос на AccountServer
Ответ не пришел
Ответ пришел
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
План лекции
29
1. Thread, Runnable=2. Frontend и
Account Service=3. Concurrent
Collections=4. Message System
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
Synchronized vs. Concurrent
31
Concurrent ― предназначена для работы с несколькими потоками,но не синхронная (без использования synchronized)G
ConcurrentHashMap ― concurrent
java.util.Hashtable ― synchronized
synchronized ― гарантия, что только один поток работает с элементом
Concurrent ― разрешено одновременное чтение и безопасная запись
Concurrent Collections
32
CopyOnWriteArrayListG G копирование при вставке в ArrayList
CopyOnWriteArraySetG G Set интерфейс над CopyOnWriteArrayList
ConcurrentHashMapG G thread safe HashMap
ConcurrentSkipListMapG G ключи уникальны и отсортированы
ConcurrentSkipListSetG G set на базе ConcurrentSkipListMap
Контейнеры безопасные для многопоточного доступа
Очереди безопасные для многопоточного доступа
Concurrent Queues
33
BlockingQueueG G G G очередь с ограничениме размера
ConcurrentLinkedQueueG G G thread safe очередь
LinkedBlockingQueueArrayBlockingQueue
BlockingDequeG G G G двухсторонняя «очередь»ArrayBlockingDeque
План лекции
34
1. Thread, Runnable=2. Frontend и
Account Service=3. Concurrent
Collections=4. Message System
Thread-local объекты
Основная идея
Обмен сообщениями
35
Один поток кладет сообщение в коллекцию
Второй поток достает сообщение и исполняет его
Thread-Safe коллекцииБезопасная работа с элементами коллекцииОптимальная работа
Объекты на которые есть ссылки только из одного потока
MessageSystem ― объект для обмена данными
Message System
36
Одна система сообщений на процесс
Единственный объект доступный из нескольких потоков
По одной очереди сообщений на поток
Каждый поток берет свою очередь из потока и выполняет сообщенияКаждый поток имеет свой адрес
Из любого места потока можно положить сообщение в очередь по адресу
Обмен сообщениями
37
Frontend Account GService
Account GQueue
Frontend GQueue
MsgToAccountService MsgToFrontend
MessageSystem
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(); }
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); }
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); }
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); } }
Иерархия сообщений
42
Msg
MsgToAS MsgToFrontend
MsgUpdateUserIdMsgGetUserId
- Address from- Address to
- String name- Integer sessionId
- Integer sessionId- Integer userId
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); } }
MessageSystem ничего не знает о Frontend и AccountService
Все что нужно MessageSystem это Address, Abonent и Msg
Можно добавлять дополнительные сервисы
Абстракция
44
Address Service
45
Часть Message System которая знает адреса абонентов
Может вернуть адрес Account сервиса и Frontend
Производит балансировку, если сервисов несколько
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
Обмен сообщениями
47
frontend account service
get Id from Storage
MsgGetUserId
MsgUpdateUserId
Hello user!
submit name
wait for auth
check state
wait for auth
check state
Метод run()
48
public void run() { while (true) { messageSystem.execForAbonent(this); Thread.sleep(TICK_TIME); } }
Демонстрация кода
49
Переключаемся на код и смотрим как он работает
Спасибо за внимание
Виталий Чибриков [email protected]