Będzie więc dziś o chyba 2 najbardziej nierozumianych metodach klasy Object. Czyli o wait() i notify()/notifyAll().
Spróbuję to wytłumaczyć prostymi słowami ;-)
Po pierwsze wolno tych metod używać tylko w sekcji synchronized, tudzież w metodach synchronizowanych i wołać je wolno tylko z obiektów na których odbywa się synchronizacja.
public class Fred {
public static void main(String[] args) {
final Fred fred = new Fred();
final Thread t1 = new Thread(new Runnable() {
public void run() {
System.out.println("t1 start :-)");
System.out.println("t1 will wait...");
synchronized (fred) {
try {
fred.wait();
} catch (InterruptedException ie) {
System.out.println("t1 interrupted...");
}
}
System.out.println("t1 stop :-)");
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
public void run() {
System.out.println("t2 start :-)");
synchronized (fred) {
System.out.println("t2 notifyAll!!!");
fred.notifyAll();
System.out.println("t2 done ;-)");
}
System.out.println("t2 stop :-)");
}
});
t2.start();
}
}
Co się dzieje w tym programie wyżej?
Najpierw tworzymy obiekt, na którym będziemy się synchronizować, zwany jest on dla niepoznaki monitorem... OK, dokładniej monitor jest na obiekcie, ale to szczegół.
Następnie tworzymy sobie wątek t1, który ma za zadanie wypisać parę rzeczy i następnie ma wywołać na obiekcie
fred
metodę wait()
. Po tym wszystkim startujemy wątek t1 [tak naprawdę mówimy tylko, że chcielibyśmy aby ten wątek został uruchomiony], po czym tworzymy wątek t2, który ma zawołać na obiekcie fred
metodę notifyAll()
. Ma więc obudzić wszystkie wątki które czekają na sygnał na danym monitorze. Gdybyśmy w tym miejscu zawołali notify()
to JVM wybudziłaby "któryś" z wątków wiszących na tym monitorze.W większości systemów wynik działania programu będzie wyglądał tak:
t1 start :-)
t1 will wait...
t2 start :-)
t2 notifyAll!!!
t2 done ;-)
t1 stop :-)
t2 stop :-)}
Choć mogą być i takie gdzie t2 wystartuje przed t1, znotyfikuje wszystkie wątki wiszące na monitorze [czyli 0], a dopiero wtedy t1 zacznie czekać ;-) Choć jest to jednak mało prawdopodobne. Wątki nie są deterministyczne, ale z dużym przybliżeniem można często bazować na zdrowym rozsądku.
Zamiast używać
fred
a jako jawnego monitora moglibyśmy po prostu w klasie Fred
stworzyć 2 metody:synchronized void method1() {
try {
wait();
} catch (InterruptedException ie) {
}
}
synchronized method2() {
notifyAll();
}
i wołać je z naszych wątków.
Teraz tłumaczenie do czego to się może przydać ;-)
Mamy np. coś co chcemy by wykonywało się co najmniej raz na 5 minut, ale w razie potrzeby żebyśmy mogli to wywołać na żądanie. Może chodzić np. o wysyłanie maili, możemy np. zdecydować, że normalne maile są kolejkowane i raz na 5 minut w orgii mailowej wysyłamy je wszystkie, a maile o błędach czy np. z przypomnieniem haseł wysyłane są na bieżąco.
Wtedy w naszym mailerze możemy użyć
wait()
i notify()
[a dokładniej wait(int)
]...choć i tu mogą pojawić się problemy, możemy np. nieszczęśliwie znotyfikować 0 wątków bo akurat wątek pocztowy wysyła maile....
A co gdy chcemy by wątek poczekał na inny? ;-)
Wtedy używamy metody
join()
i tym razem bez synchronizacji i wołamy ją na wątku na który chcemy poczekać.Jeżeli chcemy czekać pewien czas, a później nawet jeśli wątek na który czekamy się jeszcze nie skończył chcemy działać dalej to używamy
join(int)
.Gdy chcemy by wątek poszedł spać na zadany czas i nie interesuje nas to by go budzić, wtedy używamy
sleep(int)
.Używajac
wait()
, wait(int)
, join()
, join(int)
i sleep(int)
musimy łapać InterruptedException.Kiedy ten wyjątek może zostać rzucony? Wtedy gdy wątek który woła daną metodę zostanie przerwany. Przyznam, że jedyną mi znaną tego przyczyną może być zawołanie na tym wątku metody
interrupt()
, ale zapewne JVM jest w stanie czasem spowodować ten wyjątek w innych okolicznościach ;-)Dlatego gdy chcecie by wątek spał/czekał/oczekiwał przez zadany czas to bezpieczniej wołać te metody w pętli i sprawdzać czy zadany czas już minął.
Nie do końca jestem pewien czy to dobrze wytłumaczyłem.... ;-) Jakby co to pytać w komentarzach :-)
Podobne postybeta
Dodajmy 3 klasy do SDK Java'y ;-)
Kwiatek ;-)
Modale dobre - confirm dla Androida :-)
Potfór ;-) czyli generator z yield w Java'ie
Przepływ sterowany danymi - A takie Java'owe coś ;-)
Brak komentarzy:
Prześlij komentarz