2. План лекции
2
1. Thread, Runnable=
2. Frontend и
Account Service=
3. Concurrent
Collections=
4. Message System
3. Processes and Threads
3
Процессы
Приложение со своим набором run-time ресурсов и собственной памятью
Взаимодействие через Inter Process Communication ресурсы
Можно запускать на нескольких компьютерах
Потоки
«Живут» в одном процессе
Старт приложения – создание main потока
Используют общую память (heap) и другие ресурсы приложения
Потоки могут порождать другие потоки и взаимодействовать с ними
4. Что такое поток?
4
Поток
Объект, у класса которого есть методы start() и run()
После вызова метода start() будет выполнен run()
Метод run() будет выполнен в своем стеке
8. 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();
}
}
9. 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();
}
}
10. Runnable
Runnable vs Thread
10
Runnable класс нужно передавать в конструктор Thread объекта
Можно наследовать класс отличный от Thread
Thread
Содержит методы управления потоком
Thread thread = Thread.currentThread();
Текущий Thread object можно получить в любом месте кода
11. Доступ к объекту потока
Текущий 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();
Некоторые методы
12. sleep and interrupt
Если нужно остановить выполнение потока
Thread.sleep(1000) – остановит выполнение потока на 1
секунду
Если нужно прервать выполнение потока
thread.interrupt() – пошлет прерывание потоку thread
try {
Thread.sleep(5000);
} catch (InterruptedException e) { // We've been interrupted.
return;
}
G
for (int i = 0; i < inputs.length; i++) {
heavyTask(inputs[i]);
if (Thread.interrupted()) { // We've been interrupted.
return;
}
} 12
13. Если надо остановить текущий поток до окончания другого
потока
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!");
}
}
14. Взаимодействие потоков
У потоков общий Heap
Можно передать в два потока ссылку на один объект
Потоки смогут менять общий объект и взаимодействовать через
него
14
Как осуществить взаимодействие между
потоками?
15. Java memory model
Memory model + volatile
15
volatile – не кэшировать, всегда считывать из общей памяти
Описывает то, как потоки должны взаимодействовать через общую память
Кэширование значений в многопроцессорных средах
Изменение порядка операций для оптимизации
Основные проблемы
final – не изменять значение переменной
synchronized – отметить участок кода доступный только одному треду
Инструменты для решения
16. Synchronization
Демонстрация работы кода ThreadInterference.example();
Возможные ошибки одновременного
доступа
Thread Interference – потеря результата
Memory Consistency Errors – ошибочное состояние общей памяти
16
20. Служба в отдельном потоке
20
private boolean needDoSomething;
G
public void run() {
while(true){
if(needDoSomething){
doSomething();
}
Thread.sleep(1000);
}
}
21. wait() and notify()
wait(), notify() и notifyAll() ― методы класса Object
object.wait() ― ждать в текущем потоке, пока не придет notify()
object.notify() ― сигнал «продолжить» первому кто начал wait()
object.notifyAll() ― сигнал «продолжить» всем кто начал wait()
Демонстрация работы кода RandomRunExample.example();
21
22. План лекции
22
1. Thread, Runnable=
2. Frontend и
Account Service=
3. Concurrent
Collections=
4. Message System
23. UserSession
23
UserSession содержит:
Каждому, кто пришел на сервер - UserSession
Каждой UserSession – sessionId (из HttpSession)
String sessionId
String userName
Long userId
На Frontend-е
Map<String, UserSession> sessionIdToSession;
24. В одном потоке
24
Назначаем Id для пользовательской сесcии
В методе handle() спрашиваем у AccountService userId по имени
Создаем объект Accounter, который будет скрывать авторизацию
Ждем пока AccountService прочитает эти данные из базы
Создаем на основе сесcии страницу и отдаем ее браузеру
Сохраняем в объекте сессии данные о пользователе
Спрашиваем у пользователя имя
Авторизация
26. Frontend и Account Service
26
Frontend создает пользовательскую сессиюG
Frontend возвращает страницу созданную на основе сесcии в браузерG
Frontend запрашивает у Account Service данные по авторизацииG
Когда данные приходят, Frontend меняет состояние сессии
Разведем работу с пользователем и AccountService по разным потокамG
Frontend ― поток который работает с пользователямиG
AccountService ― поток который работает с авторизацией
27. Состояния
27
Ответ 2: sessionId + «Ждите авторизации»
Запрос 0: Первый запрос страницы.
Запрос 2: sessionId
Запрос 3: sessionId
Ответ 3: sessionId + «Ваше имя» + userName + « ваш Id: » + userId
Ответ 0: sessionId + «Введите имя»
Запрос 1: sessionId + Имя
Ответ 1: sessionId + «Ждите авторизации»
Запрос на
AccountServer
Ответ не пришел
Ответ пришел
28. 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. План лекции
29
1. Thread, Runnable=
2. Frontend и
Account Service=
3. Concurrent
Collections=
4. Message System
30. Atomic
30
java.util.concurrent.atomic
AtomicBoolean
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
31. Synchronized vs. Concurrent
31
Concurrent ― предназначена для работы с несколькими потоками,
но не синхронная (без использования synchronized)G
ConcurrentHashMap ― concurrent
java.util.Hashtable ― synchronized
synchronized ― гарантия, что только один поток работает с элементом
Concurrent ― разрешено одновременное чтение и безопасная запись
32. Concurrent Collections
32
CopyOnWriteArrayListG G копирование при вставке в ArrayList
CopyOnWriteArraySetG G Set интерфейс над CopyOnWriteArrayList
ConcurrentHashMapG G thread safe HashMap
ConcurrentSkipListMapGG ключи уникальны и отсортированы
ConcurrentSkipListSetG G set на базе ConcurrentSkipListMap
Контейнеры безопасные
для многопоточного доступа
33. Очереди безопасные
для многопоточного доступа
Concurrent Queues
33
BlockingQueueGG G G очередь с ограничениме размера
ConcurrentLinkedQueueG G G thread safe очередь
LinkedBlockingQueue
ArrayBlockingQueue
BlockingDequeG G G G двухсторонняя «очередь»
ArrayBlockingDeque
34. План лекции
34
1. Thread, Runnable=
2. Frontend и
Account Service=
3. Concurrent
Collections=
4. Message System
35. Thread-local объекты
Основная идея
Обмен сообщениями
35
Один поток кладет сообщение в коллекцию
Второй поток достает сообщение и исполняет его
Thread-Safe коллекции
Безопасная работа с элементами коллекции
Оптимальная работа
Объекты на которые есть ссылки только из одного потока
36. MessageSystem ―
объект для обмена данными
Message System
36
Одна система сообщений на процесс
Единственный объект доступный из нескольких потоков
По одной очереди сообщений на поток
Каждый поток берет свою очередь из потока и выполняет
сообщения
Каждый поток имеет свой адрес
Из любого места потока можно положить сообщение в очередь по
адресу
38. 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();
}
39. 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);
}
40. 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);
}
41. 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);
}
}
43. Message System
43
private Map<Address, ConcurrentLinkedQueue<Msg>> messages
= new HashMap<Address,
ConcurrentLinkedQueue<Msg>>();
G
G
public void sendMessage(Msg message){
Queue<Msg> messageQueue = messages.get(message.getTo());
messageQueue.add(message);
}
G
public void execForAbonent(Abonent abonent) {
Queue<Msg> messageQueue = messages.get(abonent.getAddress());
while(!messageQueue.isEmpty()){
Msg message = messageQueue.poll();
message.exec(abonent);
}
}
44. MessageSystem ничего не знает о Frontend и AccountService
Все что нужно MessageSystem это Address, Abonent и Msg
Можно добавлять дополнительные сервисы
Абстракция
44
45. Address Service
45
Часть Message System которая знает адреса абонентов
Может вернуть адрес Account сервиса и Frontend
Производит балансировку, если сервисов несколько
46. 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 можно хранить в MessageSystemG
G
Address аккаунт сервера для пользователя можно хранить в UserSession
47. Обмен сообщениями
47
frontend account service
get Id from Storage
MsgGetUserId
MsgUpdateUserId
Hello user!
submit name
wait for auth
check state
wait for auth
check state
48. Метод run()
48
public void run() {
while (true) {
messageSystem.execForAbonent(this);
Thread.sleep(TICK_TIME);
}
}