Lekcja 4

Wątki I

Czasem potrzebujemy robić kilka rzeczy w tym samym czasie, np. ściągać na egzaminie i monitorować położenie egzaminatora. Za pomocą wątków jesteśmy w stanie przeplatać wykonywanie różnych instrukcji (np. dwie pętle wykonują się jednocześnie).

I. Klasa Thread


// Tworzenie nowego wątku:
Thread thread0 = new Thread(new Runnable() {
    public void run() {
        // kod
    }
});

// Spróbujmy zapisać to samo za pomocą lambda-wyrażenia
Thread thread1 = new Thread(() -> {
    // kod
});

// Wątek trzeba uruchomić.
// Samo utworzenie obiektu nie wystarczy.
thread0.start();
thread1.start();
Zadanie 0

Stwórz 2 wątki. Pierwszy niech wypisuje liczby od 0 do 9 za pomocą System.out, a drugi niech robi to za pomocą System.err. Niech wątki wypiszą po 20 takich serii.

Użyj metody print zamiast println.

Uruchom program kilka razy. Czy wynik jest taki sam? Z czego to wynika?

Zadanie 1

Stwórz pętlę, która będzie wypisywała liczby od 0 do 1 000 000.

Następnie zbadaj, ile czasu wykonuje się taki program. Możesz użyć metody System.currentTimeMillis(), która zwraca liczbę milisekund od 1 stycznia 1970 0:00 UTC.

Szukamy różnicy czasu sprzed i po wykonania pętli.

Zapisz gdzieś uzyskane czasy z 3 prób.

Następnie stwórz 2 wątki. Jeden będzie wypisywać liczby parzyste, drugi nieparzyste, z przedziału od 0 do 1 000 000.
Niech każdy wątek poda, ile czasu zajęło jego zadanie.

Dokonaj 3 prób i porównaj z poprzednią implementacją (bez użycia wątków).

II. Wątki a okno

Zadanie 2

Przygotuj okno rysujące prostokąt o wymiarach 30x30 w punkcie x, y.

x oraz y powinny być parametrami obiektu, a nie zmiennymi lokalnymi.

Uruchom wątek, który będzie zmieniał wartość x oraz y (np. zwiększał o 1).

Zadanie 3

Spraw, żeby kwadrat odbijał się od krawędzi okna.

Podpowiedź: jeśli wartość x okaże się być za duża, to muszę w wątku zacząć wartość x zmniejszać.

III. Przeplatanie instrukcji


Zadanie 4

Przeanalizuj kod dany poniżej. Jakich spodziewasz się wyników?

Uruchom program kilkukrotnie.

Który fragment kodu jest krytyczny? Który nie może być w trakcie przeplatany z wykonywaniem innego wątku?

Dodaj słowo kluczowe "synchronized" we właściwym miejscu (są 2 sposoby).

class AddingThread extends Thread {

    private Counter c;

    public AddingThread(Counter c) {
        this.c = c;
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            c.inc(i);
        }
    }
}

class Counter {
    private int x = 0;
    void inc(int value) {
        int sum = x + value;
        x = sum;
    }
    public String toString() {
        return x + "";
    }
}

public class Main {

    public static void main(String[] args) {
        Counter c = new Counter();
        AddingThread at0 = new AddingThread(c);
        at0.start();
        AddingThread at1 = new AddingThread(c);
        at1.start();
        AddingThread at2 = new AddingThread(c);
        at2.start();
        System.out.println("Counter.x=" + c);
    }
}
Słowo kluczowe synchronized
  1. Może poprzedzić deklarację typu zwracanego przez metodę.
  2. Może być użyte jako blok.
  3. Jeśli zostanie na coś nałożone, to pewna grupa instrukcji traktowana jest jako "nie do przerwania w trakcie wykonywania".
// synchronized dla metody
public synchronized void method() {
    //...
}

// synchronized na obiekt
synchronized(object) {
    //...
}
Zadanie 5

Utwórz klasę Account(long balance). Jest reprezentacją konta bankowego przechowującego środki pieniężne.

Bank to taki byt, który posiada pewną pulę środków, tj. sumę pieniędzy ze wszystkich kont. Załóżmy, że mamy dwa konta na start o środkach 10 000 każde.

Zadanie polega na przeprowadzaniu równolegle 8 transakcji (na tych dwóch kontach) na losowe kwoty.

Gdy wszystkie transakcje zostaną policzone, suma środków ze wszystkich kont ma wciąż wynosić tyle, ile na początku.