6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 1
6. W
ą
tki
6.1 Poj
ę
cie w
ą
tku programu
6.2 Klasy
Timer
,
TimerTask
6.3 Klasa
Thread
6.4 Synchronizacja pracy w
ą
tków
6.5 Grupowanie w
ą
tków
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 2
6.1 W
ą
tki programu
1) Poj
ę
cie w
ą
tku
W
ą
tek ("thread")
to osobna sekwencja wykonania instrukcji programu
w ramach jednego programu, wykonywana
współbie
ż
nie
z innymi
w
ą
tkami tego programu.
W odró
ż
nieniu od procesu w
ą
tek posiada jedynie swój
kontekst
wykonania – stos, rejestry i licznik rozkazów
– ale współdzieli on
pami
ęć
dynamiczn
ą
(„stert
ę
”) programu z innymi w
ą
tkami tego
programu.
W
ą
tki znajduj
ą
zastosowanie tam, gdzie rozwi
ą
zanie zadania mo
ż
e
by
ć
zrealizowane przez
algorytm równoległy
. Przykłady zada
ń
dla
w
ą
tków: obliczenie liczb pierwszych, sortowanie danych, wykonywanie
animacji, itp.
Dzi
ę
ki
w
ą
tkom
realizujemy
mechanizm
asynchronicznego
wywoływania i wykonywania metod w
ramach jednego programu.
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 3
6.2 Klasy
Timer
i
TimerTask
Klasy
java.util.Timer
(
extends Object
)
i
TimerTask
(extends Object
implements Runnable)
s
ą
po
ż
yteczne do wyznaczania czasu wznowienia
w
ą
tku po pewnej przerwie.
Uwaga: dla programu posiadaj
ą
cego GUI realizowany w
Swing
-u
odpowiedni
ą
klas
ą
jest
javax.swing.Timer
zamiast
java.util.Timer
.
Metody klasy
Timer
przeznaczone do
wybierania zada
ń
:
// Wykonanie zadania po upływie czasu lub o dokładnej porze:
schedule (TimerTask task, long delay)
schedule (TimerTask task, Date time)
// Wielokrotne wznawianie wykonania co okres czasu:
// - "mi
ę
kkie" dotrzymanie okresu – gdy poprzednie wykonanie si
ę
zako
ń
czyło
schedule (TimerTask task, long delay, long okres)
schedule (TimerTask task, Date time, long okres)
//- ustalony na sztywno okres –bez wzgl
ę
du na zako
ń
czenie poprzedniego
scheduleAtFixedRate (TimerTask task, long delay, long okres)
scheduleAtFixedRate (TimerTask task, Date firstTime, long okres)
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 4
Jednokrotne asynchroniczne wykonanie zadania
po upływie czasu
Przykład.
Zastosujemy klasy
Timer
i
TimerTask
dla asynchronicznego
wykonania zadania po upływie 5 sekund.
import java.util.Timer;
import java.util.TimerTask;
public class Przypomnienie
{
// Klasa główna naszego zadania
Timer timer;
public Przypomnienie(int sekundy) {
// Konstruktor klasy głównej
timer = new Timer();
// (3) Tworzymy w
ą
tek - obiekt klasy
Timer
timer.schedule(new PrzypomTimerTask(), sekundy*1000);
// (4)
// (4) Tworzymy obiekt naszego typu pochodnego od
TimerTask
i
// wybieramy go do wykonania po upływie 5000 ms
}
// W klasie głównej definiujemy klas
ę
wewn
ę
trzn
ą
-
// (1) definiujemy własn
ą
klas
ę
pochodn
ą
po
TimerTask
:
class PrzypomTimerTask extends TimerTask {
public void run() {
// (2) Metoda
run()
wykonuje zadanie w
ą
tku
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 5
System.out.println("Czas minął!");
timer.cancel();
// Zako
ń
cz w
ą
tek – obiekt
timer
}
}
public static void main(String args[]) {
// Metoda main() klasy głównej
System.out.println("Wybieranie zadania.");
new Przypomnienie(5);
// Konstrukcja obiektu klasy głównej
System.out.println("Zadanie wybrane.");
}
}
Wynik:
Zadanie wybrane.
Czas minął
Drugi napis pojawi si
ę
po 5 sekundach po pierwszym.
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 6
Jednokrotne asynchroniczne wykonanie zadania
o okre
ś
lonej porze
Przykład.
Wykonanie zadania o godzinie 23:01. W tre
ś
ci konstruktora
naszej klasy
Przypomnienie
:
public Przypomnienie(int sekundy) {
// Pobierz dzisiejsz
ą
dat
ę
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 23);
// Ustaw godzin
ę
calendar.set(Calendar.MINUTE, 1);
calendar.set(Calendar.SECOND, 0);
Date time = calendar.getTime();
timer = new Timer();
// Utwórz w
ą
tek nadzoruj
ą
cy czas
// Wykonaj w
ą
tek zadania o podanej godzinie tego dnia:
timer.schedule( new PrzypomTimerTask(), time);
}
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 7
Zatrzymanie w
ą
tków klasy Timer jest mo
ż
liwe na 4 sposoby:
1. wywołanie metody
cancel()
dla obiektu klasy
Timer
(jak powy
ż
ej);
2. utworzy
ć
w
ą
tek - „demon” wywołaniem konstruktora:
new Timer(true) ;
program którego jedynymi w
ą
tkami pozostały w
ą
tki demony zako
ń
czy
si
ę
(w podanych przykładach nie mo
ż
na tak post
ą
pi
ć
, gdy
ż
czekamy
na wykonanie si
ę
zadania zleconego przez obiekt klasy
Timer
);
3. usun
ąć
wszystkie
referencje
do obiektu klasy
Timer
, mo
ż
e to
zako
ń
czy
ć
w
ą
tek obiektu
Timer
-a.
4. wywołanie metody
System.exit
zako
ń
czy cały program - w tym jego
w
ą
tki.
Przykład.
Dodanie do klasy
Przypomnienie
dzwonka i wywołanie
metody
System.exit
w celu zako
ń
czenia programu.
public class PrzypomnienieDzwonkiem {
...
public PrzypomnienieDzwonkiem (int sekundy) {
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 8
toolkit = Toolkit.getDefaultToolkit();
// W celu uzyskania dzwonka
timer = new Timer();
timer.schedule(new PrzypomTimerTask(), sekundy*1000);
}
// Klasa dla zadania w
ą
tku
class PrzypomTimerTask extends TimerTask {
public void run() {
System.out.println(Czas minął!");
toolkit.beep();
//timer.cancel();
// Teraz niepotrzebne, gdyż wołamy System.exit
System.exit(0);
// Zatrzyma w
ą
tek główny programu (i inne)
}
} ...
}
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 9
Wielokrotne, cykliczne wykonywanie zadania
Przykład.
Wybieramy wykonanie zadania cyklicznie raz na sekund
ę
.
public class DenerwujacyDzwonek {
Toolkit toolkit;
Timer timer;
public DenerwujacyDzwonek () {
toolkit = Toolkit.getDefaultToolkit();
timer = new Timer();
timer.schedule(new PrzypomTimerTask(),
0,
// początkowe oczekiwanie
1*1000);
// cyklicznie co okres czasu 1 sekundy
}
class PrzypomTimerTask extends TimerTask {
int liczbaDzwonkow = 3;
public void run() {
if (liczbaDzwonkow != 0) {
toolkit.beep();
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 10
System.out.println("Dzwoni!");
liczbaDzwonkow--;
} else {
toolkit.beep();
System.out.println("Czas minął!");
//
timer.cancel();
//Niepotrzebne - wołamy System.exit
System.exit(0);
// Zatrzyma w
ą
tek główny (i inne)
}
}
}
...
}
Wynik wykonania
:
Zadanie wybrane.
Dzwoni!
Dzwoni! // 1 sekunda po 1-szym "Dzwoni"
Dzwoni! // 1 sekunda po 2-im "Dzwoni"
Czas minął! // 1 sekunda po 3-im "Dzwoni"
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 11
6.3 Klasa
Thread
Bazowa klasa w
ą
tków to
java.lang.Thread
- wprowadza m.in.
metody
:
start()
–
rozpoczyna wykonywanie tre
ś
ci w
ą
tku zdefiniowanej w
run()
stop(), stop(Throwable obj);
- (metody niezabezpieczone) zamiast
nich nale
ż
y sprawdza
ć
warunek w
run()
i zako
ń
czy
ć
odpowiednio t
ę
metod
ę
;
sleep(long millis), sleep(long millis, int nanos)
– u
ś
pienie w
ą
tku na
pewien czas;
yield()
–
czasowo zatrzymuje w
ą
tek i ustawia go na ko
ń
cu kolejki;
getPriority(), setPriority(int) –
pobierze i ustawi priorytet w
ą
tku.
1) Metoda
run()
Implementacja
w
ą
tku
opartego
na
klasie
Thread
wymaga
zdefiniowania metody
void
run()
- zawera ona zadanie wykonywane
przez w
ą
tek. Klasa
Thread
zawiera definicj
ę
metody
run()
o
pustym
kodzie.
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 12
Klasy mog
ą
tworzy
ć
metody
run()
na
dwa sposoby
:
1. przez dziedziczenie po klasie
Thread
i nadpisanie metody
run()
;
2. przez implementacj
ę
interfejsu
Runnable
- wtedy klasa musi
zawiera
ć
implementacj
ę
metody
run()
.
Dziedziczenie po klasie
Thread
i nadpisanie
run()
- klasa
Thread
ju
ż
implementuje
Runnable
.
Przykład
.
Klasa
ProstyWatek
(implementuje
wątek)
i
klasa
DemoDwaWatki (wywołuje dwa wątki klasy ProstyWatek):
public class ProstyWatek extends Thread {
public ProstyWatek(String str) {
// Konstruktor klasy
super(str);
// To nada nazwę wątkowi
}
public void run() {
// Metoda run() zawiera zadanie wątku
for (int i = 0; i < 10; i++) {
// Wykonuje pętlę 10 razy
System.out.println(i + " " + getName());
// Wyprowadza indeks
// iteracji i nazwę wątku
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 13
try {
sleep((long)(Math.random() * 1000));
// Uśpiony przez
// pewien czas – ale maksimum 1 sekundę.
} catch (InterruptedException e) {}
}
System.out.println("Wykonane! " + getName());
// Wyprowadza
// komunikat, w tym nazwę wątku.
}
}
// Klasa
DemoDwaWatki :
public class DemoDwaWatki {
public static void main (String[] args) {
// Utwórz i uruchom w
ą
tek
new ProstyWatek ("Bahama").start();
// Utwórz i uruchom drugi w
ą
tek
new ProstyWatek ("Bermudy").start(); }
}
Wynik - na przemian wyprowadzane s
ą
wyniki kolejnych w
ą
tków:
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 14
0 Bahama
0 Bermudy
1 Bermudy
1 Bahama
2 Bahama
2 Bermudy
3 Bermudy
...
Wykonane! Bermudy
9 Bahama
Wykonane! Bahama
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 15
2) Implementacja interfejsu
Runnable
przez klas
ę
programisty
Przykład
. Aplet
Zegar
wy
ś
wietla
ć
b
ę
dzie aktualny czas i od
ś
wie
ż
a
ć
obraz co sekund
ę
- wykonuj
ą
c odpowiedni w
ą
tek. Klasa
Zegar
implementuje interfejs
Runnable
- w tym metod
ę
run()
.
import java.awt.Graphics;
import java.util.*;
import java.text.DateFormat;
import java.applet.Applet;
public class Zegar extends Applet implements Runnable {
private Thread watekZegara = null;
// Identyfikator dla obiektu wątku
public void start() {
// Metoda początkowa apletu
if (watekZegara
==
null) {
// Obiekt klasy Zegar przekaże siebie samego do konstruktora Thread –
// w ten sposób przekazana zostanie również implementacja metody
// run() właściwa dla klasy Zegar do obiektu klasy Thread.
watekZegara = new Thread(this, "Zegar");
//przekazanie do wątku
watekZegara.start();
// Uruchom wątek
.
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 16
}
}
// Zadanie w
ą
tku w metodzie
run()
public void run() {
Thread mojWatek = Thread.currentThread();
while (watekZegara
==
mojWatek) {
repaint();
// Metoda apletu wołająca metodę paint()
try {
Thread.sleep(1000);
// Samo-zawieszenie wykonywania wątku
} catch (InterruptedException e){
// Gdy JVM przerwie "nasz sen" zakończymy wykonywanie pętli.
}
}
}
// Nadpisanie metody
paint()
apletu
public void paint(Graphics g) {
Calendar cal = Calendar.getInstance();
// Pobierz dzisiejszą datę
Date date = cal.getTime();
// Pobierz czas, sformatuj go i wyświetl
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 17
DateFormat dateFormatter = DateFormat.getTimeInstance();
g.drawString(dateFormatter.format(date), 5, 10);
}
// Nadpisanie metody
stop()
klasy
Applet
, nie
Thread
public void stop() {
watekZegara = null;
//Zmienia warunek sprawdzany w metodzie run()
}
}
Uwagi
Dzi
ę
ki mechanizmowi w
ą
tków aplet
Zegar
nie blokuje wykonywania
si
ę
przegl
ą
darki.
Je
ś
li klasa ju
ż
dziedziczy po innej klasie ni
ż
Thread
(jak np.
Zegar
po
Applet
) to stosujemy 2-gie rozwi
ą
zanie - implementacj
ę
interfejsu
Runnable
.
W przeciwnym razie - stosujemy rozwi
ą
zanie 1-sze – dziedziczenie
naszej klasy po
Thread
.
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 18
3) "Cykl
ż
ycia" w
ą
tku
Nowo utworzony
Wykonywany
Zablokowany
Zako
ń
czony
Zako
ń
czenie metody run()
yield
start
Utworzenie w
ą
tku
Np. w metodzie
start()
apletu tworzony jest w
ą
tek o nazwie
watekZegara
:
public void start() {
if (watekZegara == null) {
watekZegara = new Thread(this, "Zegar");
watekZegara.start();
}
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 19
}
Po utworzeniu w
ą
tek
watekZegara
jest w stanie „
nowo utworzony
”
(„
new Thread
”)
- jest pustym obiektem klasy
Thread
bez zasobów
systemowych. Mo
ż
na go jedynie uruchomi
ć
- metod
ą
start()
(inne
odwołanie spowoduje powstanie wyj
ą
tku
IllegalThreadStateException
).
Uruchomienie w
ą
tku
public void start() {
if (watekZegara
==
null) {
watekZegara = new Thread(this, "Zegar");
watekZegara.start();
}
}
Powstaj
ą
zasoby systemowe dla w
ą
tku, wybierany jest w
ą
tek i wołana
jest dla niego metoda
run()
. W
ą
tek b
ę
dzie w stanie "
wykonywania
"
(
„
running
”
).
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 20
Zablokowanie (zawieszenie) w
ą
tku
Wykonywanie w
ą
tku zostaje
zawieszone
(stan „
zablokowany
”), gdy:
wywołana zostanie jego metoda
sleep()
;
w
ą
tek woła metod
ę
wait()
dla oczekiwania na spełnienie warunku;
w
ą
tek zablokuje si
ę
w oczekiwaniu na operacj
ę
we/wy.
Aby w
ą
tek powrócił do stanu „wykonywania” ze stanu „zablokowania”:
musi upłyn
ąć
okre
ś
lony w wywołaniu
sleep()
czas podany liczb
ą
milisekund;
inny obiekt musi powiadomi
ć
nasz u
ś
piony w
ą
tek o zmianie warunku
wywołuj
ą
c
notify
lub
notifyAll
;
operacja we/wy, blokuj
ą
ca w
ą
tek, musi si
ę
zako
ń
czy
ć
.
Ust
ą
pienie czasu procesora innym w
ą
tkom
W
ą
tek mo
ż
e samoczynnie ust
ą
pi
ć
czas procesora
bez przechodzenia
w stan
zawieszenia
wołaj
ą
c metod
ę
yield()
– przejdzie on na koniec
kolejki w
ą
tkom o jego priorytecie i da szans
ę
na wykonanie si
ę
innym
w
ą
tkom o tym samym priorytecie co ust
ę
puj
ą
cy w
ą
tek.
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 21
Zako
ń
czenie w
ą
tku
W
ą
tek ko
ń
czy si
ę
samodzielnie gdy zako
ń
czy si
ę
metoda
run().
W przykładzie z apletem
Zegar
o zako
ń
czeniu p
ę
tli metody
run()
decyduje warunek:
while (watekZegara
==
mojWatek) {
Gdy przegl
ą
darka opuszcza stron
ę
, na której wykonuje si
ę
aplet wtedy
woła ona metod
ę
stop()
dla apletu. W aplecie
Zegar
oznacza to,
ż
e:
public void stop() {
watekZegara = null;
// Zmieniamy warunek wykonywania się run()
}
Teraz metoda
run()
zako
ń
czy si
ę
-> w
ą
tek jest w stanie „
zako
ń
czony
”.
Metoda klasy
Thread: isAlive()
boolean
isAlive()
Wynik zwracany przez
isAlive
:
true
- gdy w
ą
tek jest w stanie „wykonywania” lub „zablokowania”;
false
- gdy w
ą
tek jest w stanie
„
nowo utworzony
” lub
„zako
ń
czony”.
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 22
4) Priorytet w
ą
tku - szeregowanie
Priorytet w
ą
tku decyduje o przydzieleniu czasu procesora jednemu z
w
ą
tków b
ę
d
ą
cych w stanie „wykonywania” – wybraniu go spo
ś
ród
konkuruj
ą
cych w
ą
tków.
Java
realizuje
deterministyczny
algorytm
szeregowania
(„scheduling”) w oparciu o priorytety:
W
ą
tek dziedziczy swój priorytet od w
ą
tku który go utworzył.
Metoda
setPriority
umo
ż
liwia zmian
ę
priorytetu - mo
ż
liwe s
ą
warto
ś
ci
od
MIN_PRIORITY
do
MAX_PRIORITY
(stałe zdefiniowane w
Thread
).
Wybierany jest w
ą
tek o najwy
ż
szym priorytecie - z dwóch równych
nast
ę
pny z kolejki FIFO dla danego priorytetu.
W
ą
tek wykonuje si
ę
w procesorze dopóki nie zajdzie jeden z
przypadków:
Pojawi si
ę
w
ą
tek o wy
ż
szym priorytecie - w stanie „
wykonywania
”
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 23
W
ą
tek ust
ę
puje sam (metoda
yield
)
lub ko
ń
czy si
ę
jego metoda
run
.
W systemach pracuj
ą
cych z podziałem czasu w
ą
tków – gdy jego
kwant czasu ko
ń
czy si
ę
(zale
ż
y to od implementacji klasy
Thread
).
Wtedy kolejny w
ą
tek b
ę
dzie wybrany do wykonania.
Algorytm wyboru jest
wywłaszczaj
ą
cy
- z chwil
ą
pojawienia si
ę
w
ą
tku
o wy
ż
szym priorytecie wykonywany w
ą
tek zostaje wywłaszczony.
Java
nie realizuje podziału czasu
, w
ą
tek nie zostanie wywłaszczony
przez inny w
ą
tek o tym samym priorytecie. Jednak implementacja
klasy
Thread
w danym systemie mo
ż
e realizowa
ć
prac
ą
z podziałem
czasu CPU.
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 24
6.4 Synchronizacja pracy w
ą
tków
Do tej pory rozpatrywali
ś
my w
ą
tki, które mog
ą
wykonywa
ć
si
ę
asynchronicznie
wzgl
ę
dem
siebie.
Jednak
w
ą
tki
mog
ą
wykorzystywa
ć
wyniki pracy
innych w
ą
tków. W szczególno
ś
ci w
ą
tki
mog
ą
dzieli
ć
te same dane – jednocze
ś
nie odwoływa
ć
si
ę
do nich
.
Wtedy ich praca musi by
ć
synchronizowana.
Przykład.
Producent
generuje dane, zapisuje je do obiektu sklad klasy Skladzik a
te s
ą
nast
ę
pnie odczytywane i "konsumowane" przez
konsumenta
.
Niech producent generuje liczb
ę
pomi
ę
dzy 0 a 9 (wł
ą
cznie),
zapami
ę
tuje j
ą
w obiekcie klasy
Skladzik
i wy
ś
wietla t
ę
liczb
ę
.
Nast
ę
pnie "zasypia" na losowo dobierany czas (pomi
ę
dzy 0 a 100 ms)
i znowu powtarza cykl generacji danych.
public class Producent extends Thread {
private Skladzik sklad;
private int number;
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 25
public Producent (Skladzik c, int number) {
sklad = c;
this.number = number;
}
public void run() {
for (int i = 0; i < 10; i++) {
skład.put(i);
System.out.println("Producent #" + this.number + " położył: " + i);
try {
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
}
}
}
Konsument pobiera wszystkie liczby jak szybko to jest mo
ż
liwe.
public class Konsument extends Thread {
private Skladzik sklad;
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 26
private int number;
public Konsument(Skladzik c, int number) {
sklad = c;
this.number = number;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = sklad.get();
System.out.println("Konsument #" + this.number+"pobrał: "+ value);
}
}
}
// Klasa główna programu
public class ProducentKonsumentTest {
public static void main(String[] args) {
Skladzik c = new Skladzik();
Producent p1 = new Producent(c, 1);
// Utwórz producenta
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 27
Konsument c1 = new Konsument(c, 1);
// Utwórz konsumenta
p1.start();
// Uruchom wątek producenta
c1.start();
// Uruchom wątek konsumenta
}
}
Producent i konsument wymieniaj
ą
dane poprzez
wspólny obiekt
Skladzik
. Ale obie klasy "
Konsument
" i "
Producent
"
nie synchronizuj
ą
jawnie swoich działa
ń
, nie zapewniaj
ą
tego,
ż
eby konsument
otrzymywał ka
ż
d
ą
dan
ą
tylko raz
.
Dlatego w tym przykładzie synchronizacja
musi by
ć
zrealizowana na
"niskim poziomie"
przez metody "
get
" i "
put
" klasy
Składzik
. W innym
przypadku łatwo mo
ż
e powsta
ć
"
hazard
" w dost
ę
pie do obiektu przez
oba w
ą
tki.
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 28
Sposoby synchronizacji:
zablokowanie innemu w
ą
tkowi dost
ę
pu do obiektu
(sekcja
krytyczna)
– kwalifikator metody
synchronized
.
powiadamianie si
ę
w
ą
tków - metody klasy
Thread
-
wait, notify
,
notifyAll
.
Sekcja krytyczna
Przykład (c.d.)
W podanym przykładzie metody
put
i
get
klasy
Skladzik
tworz
ą
sekcje krytyczne.
public class Skladzik {
private int contents;
private boolean available = false;
public synchronized int get() {
...
}
public synchronized void put(int value) {
...
}
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 29
}
Z ka
ż
dym obiektem klasy
Skladzik
zwi
ą
zany b
ę
dzie
semafor
. Je
ś
li
wołana jest metoda synchronizowana to obiekt zostaje zablokowany.
Inny w
ą
tek nie mo
ż
e wykona
ć
metody synchronizowanej
na tym
samym obiekcie do momentu odblokowania obiektu. Uniemo
ż
liwia to
powstanie hazardu w dost
ę
pie do obiektu.
Metody
notifyAll
i
wait
W
ą
tki musz
ą
si
ę
móc powiadamia
ć
,
ż
e czekaj
ą
na nie dane (
wait
) i
ż
e
dane zostały dostarczone (
notify, notifyAll
).
Przykład (c.d.)
Nowe definicje metod
get
i
put
klasy
Skladzik
public synchronized int get() {
while (available == false) {
try {
wait();
// Oczekuj na położenie danej przez producenta
} catch (InterruptedException e) {
}
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 30
}
available = false;
notifyAll();
// Powiadom producenta o pobraniu danej
return contents;
}
public synchronized void put(int value) {
while (available == true) {
try {
wait();
// Oczekuj na pobranie danej przez konsumenta
} catch (InterruptedException e) {
}
}
contents = value;
available = true;
notifyAll();
// Powiadom konsumenta o położeniu danej
}
Metod
wait
zwalnia semafor "trzymany" przez "Konsumenta" na
obiekcie "sklad" i oczekuje na powiadomienie od "Producenta".
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 31
Metoda
notifyAll
budzi do
ż
ycia wszystkie w
ą
tki, które oczekuj
ą
na
dany obiekt (w tym przypadku "sklad"). Konkuruj
ą
one wtedy o
dost
ę
p do obiektu.
Metoda
notify
w klasie
Object
: "budzi" dowolny w
ą
tek spo
ś
ród
oczekuj
ą
cych.
W klasie
Object
zdefiniowano trzy wersje metody
wait
:
wait()
- oczekuje w niesko
ń
czono
ść
na nadej
ś
cie powiadomienia;
wait (long timeout)
- oczekuje najwy
ż
ej liczb
ę
timeout
milisekund;
wait (long timeout, int nanos)
- oczekuje najwy
ż
ej liczb
ę
timeout
milisekund plus
nanos
nanosekund.
Nale
ż
y unika
ć
"wygłodzenia” i zakleszcze
ń
w
ą
tków.
"Wygłodzenie" w
ą
tku wyst
ą
pi gdy jeden lub wi
ę
cej w
ą
tków nie
otrzymuje dost
ę
pu do zasobów.
Zakleszczenie to ko
ń
cowe stadium „wygłodzenia” - w
ą
tki czekaj
ą
na
spełnienie niemo
ż
liwego warunku.
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 32
6.5 Grupowanie w
ą
tków
Klasa
java.lang.ThreadGroup -
umo
ż
liwia zebranie w
ą
tków w jeden
obiekt i jednoczesn
ą
manipulacj
ę
nimi wszystkimi.
Podczas tworzenia w
ą
tku zostaje on przydzielony do jakie
ś
grupy i
musi ju
ż
tam pozosta
ć
.
Domy
ś
lna grupa w
ą
tków dla aplikacji to jest
main
.
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 33
Utworzenie nowej grupy w
ą
tków o nazwie
name
:
•
ThreadGroup(String name)
•
ThreadGroup(ThreadGroup parent, String name)
Np. utworzenie grupy jako elementu domy
ś
lnej grupy w
ą
tków
main
:
ThreadGroup myThreadGroup = new ThreadGroup(“Moja grupa");
Je
ś
li podczas tworzenia nowego w
ą
tku nie specyfikujemy jego grupy to
zostanie on zaliczony do grupy tego w
ą
tku, który go utworzył.
Trzy konstruktory klasy
Thread
umo
ż
liwiaj
ą
utworzenie i dodanie w
ą
tku
do jawnie podanej grupy:
public Thread(ThreadGroup group, Runnable runnable)
public Thread(ThreadGroup group, String name)
public Thread(ThreadGroup group, Runnable runnable, String name)
Np.
Utworzenie w
ą
tku w nowej grupie:
Thread myThread = new Thread(myThreadGroup, "watek w grupie");
Okre
ś
lenie grupy dla w
ą
tku - metoda
getThreadGroup
, np.:
theGroup = myThread.getThreadGroup();
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 34
Metody klasy
ThreadGroup
Zarz
ą
dzanie kolekcj
ą
activeCount()
- podaje liczb
ę
aktywnych w
ą
tków w grupie;
activeGroupCount()
– podaje liczb
ę
aktywnych grup w
ą
tków w grupie
.
Klasa implementuje interfejs
Enumerate
, co pozwala m.in. na pobranie
listy aktywnych w
ą
tków tej grupy.
Np.
public class EnumerateTest {
public void listCurrentThreads() {
ThreadGroup currentGroup =
Thread.currentThread().getThreadGroup();
int numThreads = currentGroup.activeCount();
Thread[] listOfThreads = new Thread[numThreads];
currentGroup.enumerate(listOfThreads);
for (int i = 0; i < numThreads; i++)
System.out.println("Wątek #" + i + " = " + listOfThreads[i].getName());
}
}
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 35
Przetwarzanie grupy w
ą
tków
getMaxPriority, setMaxPriority
// Ustawianie priorytetu
getDaemon, setDaemon
getName
getParent, parentOf
toString
Np.
public class MaxPriorityTest {
public static void main(String[] args) {
ThreadGroup groupNORM = new ThreadGroup(
// Utworzenie grupy
"Normalny priorytet grupy");
Thread priorityMAX = new Thread(groupNORM,
// Dodanie wątku
"Wątek o maksymalnym priorytecie");
// Ustaw priorytet wątku na maksymalnie możliwy (10)
priorityMAX.setPriority(Thread.MAX_PRIORITY);
// Ustaw maksymalny priorytet grupy na normalny (5)
groupNORM.setMaxPriority(Thread.NORM_PRIORITY);
System.out.println("Maksymalny priorytet grupy = " +
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 36
groupNORM.getMaxPriority());
System.out.println("Priorytet wątku = " + priorityMAX.getPriority());
}
}
Wynik wykonania:
Maksymalny priorytet grupy = 5
Priorytet wątku = 10
Metody operuj
ą
ce na stanie wszystkich w
ą
tkach grupy jednocze
ś
nie
resume() -
wznów
stop() -
zatrzymaj
suspend() -
zawie
ś
Metody te zmieniaj
ą
stan wszystkich w
ą
tków grupy jednocze
ś
nie.
Uwaga: poniewa
ż
wykorzystuj
ą
one metody
resume, stop
i
suspend
klasy
Thread
nie s
ą
one zabezpieczone przed w
ą
tkami (tzn.
jednoczesnym u
ż
yciem przez wi
ę
cej ni
ż
jeden w
ą
tek).
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 37
Metody kontroli dost
ę
pu
Obie klasy współpracuj
ą
z klas
ą
SecurityManager
.
Klasy
Thread
i
ThreadGroup
posiadaj
ą
metod
ę
checkAccess
, która
wywołuje metod
ę
checkAccess
aktualnego „security managera”,
sprawdzaj
ą
c
ą
prawo dost
ę
pu dla grupy - zgłasza wyj
ą
tek
SecurityException
je
ś
li nie jest to mo
ż
liwe.
Poni
ż
sze metody klasy
ThreadGroup
wywołuj
ą
metod
ę
checkAccess
zanim wykonaj
ą
swoj
ą
akcj
ę
(regulowany dost
ę
p):
•
ThreadGroup(ThreadGroup parent, String name)
•
setDaemon(boolean isDaemon)
•
setMaxPriority(int maxPriority)
•
stop
•
suspend
•
resume
•
destroy
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 38
Lista metod klasy
Thread
, które wywołuj
ą
checkAccess
zanim wykonaj
ą
swoj
ą
akcj
ę
:
•
konstruktory specyfikujące grupę wątków;
•
stop
•
suspend
•
resume
•
setPriority(int priority)
•
setName(String name)
•
setDaemon(boolean isDaemon)
6. W
ą
tki.
W. Kasprzak: Programowanie zdarzeniowe
6 - 39
Podsumowanie
Pakiety wspomagaj
ą
ce programowanie wielow
ą
tkowe
java.lang.Thread -
Bazowa klasa dla w
ą
tków w Javie.
java.lang.Runnable
-
Interfejs
Runnable
zawiera metod
ę
run
,
wymagan
ą
dla ka
ż
dego w
ą
tku.
java.lang.Object -
Klasa główna
Object
zawiera definicje 3 metod
przeznaczonych do synchronizacji w
ą
tków
:
wait, notify, notifyAll
.
java.lang.ThreadGroup -
Ka
ż
dy w
ą
tek nale
ż
y do jakie
ś
grupy,
zwykle skupiaj
ą
cej powi
ą
zane ze sob
ą
w
ą
tki.
java.lang.ThreadDeath
-
Wymuszenie zako
ń
czenia pracy w
ą
tku jest
mo
ż
liwe dzi
ę
ki
przekazaniu
mu obiektu klasy
ThreadDeath
.
Elementy j
ę
zyka wspomagaj
ą
ce w
ą
tki
Słowo kluczowe w Javie przeznaczone do synchronizacji w
ą
tków:
synchronized
.
Elementy
ś
rodowiska wykonania wspomagaj
ą
ce w
ą
tki
W
ś
rodowisku wykonania Javy wyst
ę
puje szeregowanie w
ą
tków.