Przykłady poniżej jak stworzyć wątek jako rozszerzenie klasy Thread i jako interfejs Runnable. Przykłady zawierają wszystkie elementy z wykładu 2 (oprócz grupowania wątków), oprawione odpowiednim komentarzem. Dla obu przypadków ów elementy się powtarzają, ale ich wywołanie może być nieco inne.
Tworzenie wątków Thread i Runnable:
- Thread:
class ThreadTest extends Thread {
// Nadanie nazwy wątkowi w konstruktorze
public ThreadTest ( String name )
{ super ( name ); }
public void run () {
for (int i = 0; i < 10; i++)
{
System.out.println(getName() + ": " + i );
// uśpienie wątku na 100 ms.
try {
sleep(100);
} catch (InterruptedException ex) {
System.out.println(ex);
}
// przerwanie wątku.
if (interrupted ()) break ;
// przekazanie sterowana.
if (i % 2 == 0) yield ();
}
System.out.println(" Thread finished .");
}
public static void main ( String [] args ) throws InterruptedException{
// Stworzenie obiektu wątku z nazwą
ThreadTest t = new ThreadTest ("Moja nazwa");
// Ustawianie piorytetu wątku – można też odpalić z konstruktora klasy // ThreadTest
t.setPriority(4);
// Ustawienie wątku jako demon – można też odpalić z konstruktora klasy // ThreadTest
t.setDaemon(true);
// Wystartowanie wątku
t.start();
// Wywołanie przerwania na wątku
t.interrupt();
// Oczekiwanie aż wątek się zakończy
try {
t.join ();
System.out.println (" Main finished .");
} catch ( InterruptedException e) {
System.out.println (" Przerwano oczekiwanie !");
}
}
}
- Runnable
class RunnableTest implements Runnable {
public void run () {
for (int i = 0; i<10; i++){
System.out.println(Thread.currentThread().getName() + ": " + i);
// uśpienie wątku na 100 ms.
try {
Thread.currentThread().sleep(100);
} catch ( InterruptedException e) {
System.out.println(" Przerwano !");
}
// przerwanie wątku.
if (Thread.currentThread().interrupted ()) break ;
// przekazanie sterowana.
// == runnable nie ma :( ==
}
System.out.println(" Runnable finished.");
}
public static void main ( String [] args ) throws InterruptedException {
// Stworzenie obiektu wątku z nazwą
RunnableTest r = new RunnableTest();
Thread t = new Thread(c, "Moja nazwa");
// Ustawianie piorytetu wątku
t.setPriority(4);
// Ustawienie wątku jako demon
t.setDaemon(true);
// Wystartowanie wątku
t.start ();
// Wywołanie przerwania na wątku
t.interrupt();
// Oczekiwanie aż wątek się zakończy
try {
t.join ();
System.out.println (" Main thread finished .");
} catch ( InterruptedException e) {
System.out.println (" Przerwano oczekiwanie !");
}
}
}
Dodatkowe info dla wątków:
Metoda interrupt() ustawia flagę
Metoda interrupted() sprawdza i czyści flagę
Metoda isInterrupted() sprawdza nie czyści flagi!
Wygenerowanie InterruptedException czyści flagę
MIN_PRIORITY 1
NORM_PRIORITY 5 - domyślne dla wszystkich wątków
MAX_PRIORITY 10
Normalnie proces aplikacji kończy się, gdy skończą się WSZYSTKIE wątki. Wątek typu DEMON kończy się, gdy skończą się wszystkie wątki niebędące demonami (na przykład główny wątek)
Wątku z poziomu programu nie da się zabić. Musi być przerwany albo sam się skończyć.
Wątki współzawodniczą o zasoby, ale mogą współpracować. Do tego potrzebują komunikacji i mogą to robić najprościej przez zmienne statyczne
public class OneOfMenyThread extends Thread{
static int zmiennaStatyczna = 0;
public void run () {
while(zmiennaStatyczna<10000)
zmiennaStatyczna++;
}
}
Odpalenie 3 takich wątków sprawi, że będą razem dodawać do zmiennaStatyczna aż ta będzie równa 10000.
Podsumowanie:
- Najczęściej realizowana za pomocą wątków
- Wbudowana w język
- Efektywna
- Reprezentacja przy pomocy obiektów
- Metody do zarządzania wątkami
- Prawdziwe, równolegle działające wątki
- Zachowanie wątków zależy od systemu operacyjnego
- Brak determinizmu
- Konieczna synchronizacja
Monitor
Mechanizm, który kontroluje dostęp do konkretnego obiektu.
Cechy:
Związane z każdym obiektem (Object) - czyli każdy obiekt ma swój monitor.
Tylko jedna zmienna condition
Monitory różnych obiektów są niezależne
public class BuforMulti implements Bufor {
private int product;
private boolean pusty = true; // zmienna condition.
synchronized public void put(int p) {
while (!pusty) {
try {
wait();
} catch (InterruptedException e) {System.out.println (e);}
}
product = p;
pusty = false;
notifyAll();
}
synchronized public int get() {
while (pusty) {
try {
wait();
} catch (InterruptedException e) {System.out.println (e);}
}
pusty = true;
notifyAll();
return product;
}
}
Trochę definicji:
condition – Warunek monitora. Powyższy monitor ma na przykład warunek (condition) pusty, który mówi, że bufor nie może być pusty. Warunek (condition) daje się jako warunek pętli while.
synchronized - „blokuje” obiekt i pozwala przeprowadzić operacje ze świadomością, że nie zostanie to przerwane (niepodzielność operacji). Obiekt może być synchronizowany w tym samym czasie tylko raz. Na dodatek wait(), notify() i notifyAll() może być tylko uruchomione w sekcji synchronized.
wait() - usypia wątek,
równocześnie
zwalniając zasoby (tak jakby wyłącza synchronized).
Dzięki wait,
oczekiwanie nie jest aktywne (czyli nie kręci się w
while(!warunek) ) tylko
pasywne (nie zabiera CPU). Tylko
3 rzeczy mogą go wybudzić wątek
z wait(). Jedną jest
interrupt()
a pozostałymi notify()
i notifyAll().
notify() - budzi jeden JAKIŚ (nie wiemy jaki) wątek, który w danym monitorze czeka w wait().
notifyAll() - budzi WSZYSTKIE wątki wiszące na wait()
Czytelnicy i pisarze
public class Czytelnia {
private int czytelnikow = 0;
private int pisarzy = 0;
private int pisarzy_czeka = 0; // dochodzi w v.2
private boolean kolej_na_pisarzy = false ; // dochodzi w v.3
synchronized public void poczatekCzytania() {
// v.1 głodzi pisarzy/faworyzuje czytelników.
// Patrz. poczatekPisania(), v.1
// while (pisarzy > 0) {
// v.2 głodzi czytelników, faworyzuje pisarzy:
// Może być tak, że pisarze będą w nieskończoność czekać
// przed czytelnią, więc czytelnicy, zgodnie z warunkim pisarzy_czeka > 0
// będą wisieć tutaj na wait()
// while ( pisarzy > 0 || pisarzy_czeka > 0) {
// v.3 kolej_na_pisarzy zapewnia proste kolejkowanie, dzięki któremu
// czytelnik i pisarz wchodzą na zmianę
while ( pisarzy > 0 || ( pisarzy_czeka > 0 && kolej_na_pisarzy ))
{
try {
wait();
} catch (InterruptedException e) {
}
}
kolej_na_pisarzy = true ; // dochodzi w v.3
czytelnikow++;
}
synchronized public void koniecCzytania() {
czytelnikow--;
notifyAll();
}
synchronized public void poczatekPisania() {
pisarzy_czeka ++; // dochodzi w v.2
// v.1 Może być tak, że czytelnicy będą w nieskończonoś wchodzić
// więc pisarze będą wiecznie wisieć tutaj na wait().
while (czytelnikow > 0 || pisarzy > 0) {
try {
wait();
} catch (InterruptedException e) {
System.out.println(e);
}
}
pisarzy_czeka--;// dochodzi w v.2
kolej_na_pisarzy = false; // dochodzi w v.3
pisarzy++;
}
synchronized public void koniecPisania() {
pisarzy--;
notifyAll();
}
}
Dla każdej v. jest inny warunek while w poczatekCzytania(). Komentarze opisują co się dzieje z czytelnikami i pisarzami dla każdej wersji.