-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy path13-concurrency.lecture
More file actions
142 lines (103 loc) · 6.44 KB
/
Copy path13-concurrency.lecture
File metadata and controls
142 lines (103 loc) · 6.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
= Днес
+ Нишки
+ Конкурентност
+ Проблеми и решения
= Нишки
== Нишка vs процес
* Нишка ще наричаме последователност от операции, които се изпълняват последователно в един процес.
* Всеки процес започва изпълнението си от главната нишка (main thread).
* Нишките могат да споделят памет директно.
* Процесите имат нужда от специални средства за комуникация (напр. pipes)
* Нишките могат да създават и пускат други нишки.
= Защо ползваме нишки
* Бързина на изпълнението
* Писане и четене от бази от данни
* Ползване на отдалечени услуги с голямо latency на мрежата
* Ползване на сериозни количества отдалечени данни / ресурси
* Сериозна сложност на изчисленията
* Schedule-ване на задачи - да започнат да се изпълняват на конкретна дата или след даден период.
* Изчакване на нужни ресурси да се освободят
* Отговаряне на потребителски събития или приоритетни системни събития
= Защо ползваме нишки
== Responsive приложение ?
:code
//This will not work
public void actionPerformed(final ActionEvent event) {
if (running) stopCounting(); else startCounting();
}
private void startCounting() {
startStopButton.setText("Stop");
running = true;
for(int count = 0; running; count++) {
timeLabel.setText(String.format("%d", count));
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
private void stopCounting() {
running = false;
startStopButton.setText("Start");
}
= Как работят нишките
{{{<img src="13-concurrency/threads.png" width="35%">}}}
= Колко нишки ни трябват?
* Колко нишки - pool size
* Определете коефициента на блокиране - bc
* # нишки = # ядра / (1 - bc)
* Определете частите, на които да разбиете задачата (partition size)
* Определете зависимостите им
* И не на последно място - начина да си споделят памет ?
= Java API за конкурентност
== Threads
Интерфейса Runnable
Функциите в Clojure са Runnable
Класът Thread
- имплементира Runnable
- може да се създаде, като приема Runnable обект за аргумент
- пускате нишка чрез метода start()
- ще разгледаме пълния живот на една нишка в следващите слайдове
- класът Thread има и статични методи които позволяват да вземете информация за текущото изпълнение, брой нишки, stackTrace и т.н.
:code
(dotimes [i 10] (.start (Thread. (fn [] (println i)))))
= Java API за конкурентност
== Threads lifecycle
{{{<img src="13-concurrency/thread-lifecycle.png" width="50%">}}}
= Проблеми с low-level API за конкурентност на Java
* Стандартно нишките не могат да се преизползват
* Работата с wait и notify е сложна и често се правят грешки
* Грануларността на синхронизиране е много трудна
* Няма как да се сложат time-out на synchronized
* Synchronized не позволява синхронно четене
* Тестването е сложно и изисква прекалено много код
= Как да ползваме новото API за конкурентност
* ExecutorService е тук, за да замени методите на Thread класа
* Когато ви трябват ключалки, ползвайте Lock интерфейса
* Когато ви трябват wait/notify, ползвайте CyclicBarriers или CountdownLatch
= Споделяне на ресурси
== The fun starts here
Дори да разделим напълно някаква част от изпълнението на програмата, винаги стигаме до момента, в който трябва да обединим резултатите и да направим нещо с тях.
Да разгледаме примера с банковата сметка...
= Възможни проблеми в работата с нишки
== Глад и deadlocks
{{{<img src="13-concurrency/dining-philosophers.jpg" width="40%">}}}
= Възможни проблеми в работата с нишки
== Race conditions
* Конкурентно използване на ресурси (където поне едната нишка модифицира състоянието)
* Just-in-time compiler optimization
* Java Memory Model
* Некоректно използване на synchronized
= Какво да правим?
* Винаги слагайте time-out когато чакате за ресурси / събития
* Винаги освобождавайте споделени ресурси след определен time-out
* Сложете ясен ред и приоритет на ползването на ресурсите
* Синхронизирайте грануларно, но пълно!
* Ползвайте volatile за да избегнете JIC оптимизации
* Ако ще влизате в дебрите на конкурентността - отделете времето да разберете модела на паметта на Java, the Memory Barrier, компилацията и т.н.
* И все пак - избягвайте споделен mutable state когато можете
= Основни подходи към споделянето на състояние
* Shared mutability
* Isolated mutability
* Pure immutability