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
W. Kasprzak: Programowanie zdarzeniowe
6 - 1
6. Wą tki.
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.
W. Kasprzak: Programowanie zdarzeniowe
6 - 2
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) W. Kasprzak: Programowanie zdarzeniowe
6 - 3
6. Wą tki.
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
W. Kasprzak: Programowanie zdarzeniowe
6 - 4
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.
W. Kasprzak: Programowanie zdarzeniowe
6 - 5
6. Wą tki.
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);
}
W. Kasprzak: Programowanie zdarzeniowe
6 - 6
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) {
W. Kasprzak: Programowanie zdarzeniowe
6 - 7
6. Wą tki.
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)
}
} ...
}
W. Kasprzak: Programowanie zdarzeniowe
6 - 8
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();
W. Kasprzak: Programowanie zdarzeniowe
6 - 9
6. Wą tki.
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"
W. Kasprzak: Programowanie zdarzeniowe
6 - 10
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.
W. Kasprzak: Programowanie zdarzeniowe
6 - 11
6. Wą tki.
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
W. Kasprzak: Programowanie zdarzeniowe
6 - 12
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:
W. Kasprzak: Programowanie zdarzeniowe
6 - 13
6. Wą tki.
0 Bahama
0 Bermudy
1 Bermudy
1 Bahama
2 Bahama
2 Bermudy
3 Bermudy
...
Wykonane! Bermudy
9 Bahama
Wykonane! Bahama
W. Kasprzak: Programowanie zdarzeniowe
6 - 14
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.
W. Kasprzak: Programowanie zdarzeniowe
6 - 15
6. Wą tki.
}
}
// 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
W. Kasprzak: Programowanie zdarzeniowe
6 - 16
6. Wą tki.
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.
W. Kasprzak: Programowanie zdarzeniowe
6 - 17
6. Wą tki.
3) "Cykl życia" wątku
yield
start
Nowo utworzony
Wykonywany
Zablokowany
Zakoń czenie metody run()
Zakończony
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();
}
W. Kasprzak: Programowanie zdarzeniowe
6 - 18
}
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”).
W. Kasprzak: Programowanie zdarzeniowe
6 - 19
6. Wą tki.
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.
W. Kasprzak: Programowanie zdarzeniowe
6 - 20
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”.
W. Kasprzak: Programowanie zdarzeniowe
6 - 21
6. Wą tki.
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”
W. Kasprzak: Programowanie zdarzeniowe
6 - 22
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.
W. Kasprzak: Programowanie zdarzeniowe
6 - 23
6. Wą tki.
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;
W. Kasprzak: Programowanie zdarzeniowe
6 - 24
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;
W. Kasprzak: Programowanie zdarzeniowe
6 - 25
6. Wą tki.
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
W. Kasprzak: Programowanie zdarzeniowe
6 - 26
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.
W. Kasprzak: Programowanie zdarzeniowe
6 - 27
6. Wą tki.
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) {
...
}
W. Kasprzak: Programowanie zdarzeniowe
6 - 28
}
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) {
}
W. Kasprzak: Programowanie zdarzeniowe
6 - 29
6. Wą tki.
}
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".
W. Kasprzak: Programowanie zdarzeniowe
6 - 30
6. Wą tki.
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.
W. Kasprzak: Programowanie zdarzeniowe
6 - 31
6. Wą tki.
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.
W. Kasprzak: Programowanie zdarzeniowe
6 - 32
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();
W. Kasprzak: Programowanie zdarzeniowe
6 - 33
6. Wą tki.
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());
}
}
W. Kasprzak: Programowanie zdarzeniowe
6 - 34
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 = " +
W. Kasprzak: Programowanie zdarzeniowe
6 - 35
6. Wą tki.
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).
W. Kasprzak: Programowanie zdarzeniowe
6 - 36
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
W. Kasprzak: Programowanie zdarzeniowe
6 - 37
6. Wą tki.
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)
W. Kasprzak: Programowanie zdarzeniowe
6 - 38
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.
W. Kasprzak: Programowanie zdarzeniowe
6 - 39