Ponoć nie było do tego pytań na „sprawozdaniu”, ale podczas obrony może jakieś paść, więc lepiej sobie to ogarnąć.
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()
Pytania na sprawozdaniu to co realizuje jakąś funkcjonalność, czyli będą podane definicje i trza dopasować funkcję. Trzeba też poprzeć kawałkiem kodu i w kodzie najlepiej pokazać co i jak.
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 whila w poczatekCzytania(). Wersje opisują co się dzieje z czytelnikami i pisarzami.
Zadania dotyczące ćwiczenia 3. Były trzy kody, wskazać które jest poprawne. Jedno głodzi pisarzy, drugie czytelników. Powiedzieć czemu dwa pozostałe rozwiązania są niepoprawne. Oraz dla niepoprawnych wskazać jak wywołać niepożądaną sytuację. Jest w kodzie pokazane.
O czwartym ćwiczeniu z instrukcji nie było mowy :D.