Lab - Wątki
Runnable
Runnable to interfejs funkcyjny w Javie, który definiuje jedną metodę run(). Reprezentuje on zadanie do wykonania w osobnym wątku, jednak w przeciwieństwie do Callable nie zwraca żadnej wartości ani nie zgłasza sprawdzanych wyjątków. Aby uruchomić kod zapisany w klasie implementującej Runnable, przekazujemy jej obiekt do konstruktora klasy Thread lub wykorzystujemy go w ramach ExecutorService, co pozwala wygodnie zarządzać wieloma zadaniami wykonywanymi współbieżnie.
Przykład:
public class MyRunnable implements Runnable { private String taskName;
    public MyRunnable(String taskName) {
        this.taskName = taskName;
    }
    
    @Override
    public void run() {
        System.out.println("Zadanie " + taskName + " wykonuje wątek: " 
                           + Thread.currentThread().getName());
    }
}Przykład uruchomienia
public class RunnableExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyRunnable("A"));
        Thread thread2 = new Thread(new MyRunnable("B"));
        thread1.start();
        thread2.start();
    }
}Krótsza forma
public class RunnableExample {
    public static void main(String[] args) {
        Runnable task = () -> {
            System.out.println("Wątek działa: " + Thread.currentThread().getName());
        };
        Thread thread = new Thread(task);
        thread.start(); // uruchamia nowy wątek
    }
}Callable
Callable<V> to interfejs funkcyjny w Javie, który posiada jedną metodę call(). Służy on do reprezentowania zadań wykonywanych w osobnych wątkach, przy czym metoda call() zwraca wynik określonego typu V oraz może zgłaszać wyjątki (Exception). Aby odebrać wartość obliczoną w wątku, korzystamy z obiektów Future, natomiast do zarządzania większą liczbą wątków stosujemy mechanizmy oferowane przez ExecutorService.
Przykład
import java.util.concurrent.Callable;
// Klasa implementująca interfejs Callable
public class MyCallable implements Callable<Integer> {
    private int number;
    public MyCallable(int number) {
        this.number = number;
    }
    @Override
    public Integer call() throws Exception {
        System.out.println("Obliczam kwadrat liczby " + number 
                           + " w wątku: " + Thread.currentThread().getName());
        return number * number;
    }
}Przykład uruchomienia
import java.util.concurrent.*;
public class CallableExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Integer> result = executor.submit(new MyCallable(5));
        System.out.println("Wynik obliczeń: " + result.get());
        executor.shutdown();
    }
}Krótsza forma
import java.util.concurrent.*;
public class CallableExample {
    public static void main(String[] args) throws Exception {
        Callable<Integer> task = () -> {
            System.out.println("Obliczam wynik w wątku: " + Thread.currentThread().getName());
            return 2 + 3;
        };
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Integer> future = executor.submit(task);
        System.out.println("Wynik obliczeń: " + future.get()); // blokuje do zakończenia obliczeń
        executor.shutdown();
    }
}Pobieranie wartości z watka wykorzystującego interfejsu Calleble odbywa się z wykorzystaniem obiektów Future<V> . Tworząc obiekt Future musimy podać ten sam typ co zwraca wątek np. 
Future<Integer> future = executor.submit(task);w powyższym przykładzie tworzymy obiekt, który ma przechować wartość Integer, jednocześnie przypisujemy mu zadanie, które ma się wykonać w wątku. Następnym krokiem jest pobranie wyniku z wątku.
Podsumowanie
Cecha
Runnable
Callable<V>
Metoda
void run()
V call() throws Exception
Zwracany wynik
brak (zawsze void)
zwraca wynik typu V
Obsługa wyjątków
tylko wyjątki niekontrolowane (RuntimeException)
może rzucać wyjątki sprawdzane (Exception)
Użycie
proste zadania bez potrzeby zwracania danych
gdy chcemy zwrócić wynik obliczeń lub obsłużyć wyjątek
Najczęstszy sposób uruchomienia
new Thread(runnable).start()
ExecutorService.submit(callable) → Future
Dodano w Javie
Java 1.0
Java 5 (wraz z pakietem java.util.concurrent)
ExecutorService
Obiekty ExecutorService pozwalają na zarządzać pulą wątków (thread pool) i uruchamiać zadania asynchronicznie, bez konieczności ręcznego tworzenia i kontrolowania wątków ( ponowne używanie, zamykanie). 
Zazwyczaj tworzy się je przez fabryczne metody klasy Executors 
Tworzenie wątków
Wykorzystując obiekt ExecutorService mamy możliwość stworzenia różnych wątków w zależności od potrzeb 
Metoda
Opis
Executors.newSingleThreadExecutor()
Jeden wątek wykonujący zadania sekwencyjnie.
Executors.newFixedThreadPool(int n)
Stała liczba wątków – dobre przy przewidywalnym obciążeniu.
Executors.newCachedThreadPool()
Dynamiczna pula wątków – dopasowuje się do liczby zadań.
Executors.newScheduledThreadPool(int n)
Pozwala uruchamiać zadania cyklicznie lub z opóźnieniem.
Dodawanie zadań do wątków
Wykorzystując obiekt ExecutroService możemy dodweaać większą liczbę zadań do naszych wątków
Zamykanie wątków
Ważnym etapem pracy z wątkami jest zamykanie ich, można wykonać to na kilka sposobów:
Podstawowe wywołanie zamknięcia wątków
executor.shutdown();Natychmiastowa wywołanie zamknięcia wątków, może powodować wyjątek
executor.shutdownNow();Lub określenie czasu po jakim mają zakończyć prace.
executor.awaitTermination(10, TimeUnit.SECONDS);Zadania
- Stwórz wątek który, będzie co trzy sekundy skanował wybrany folder w poszukiwaniu plików. Wątek powinien informować o zmianach w podanej lokalizacji 
- Stwórz wątek, który będzie co stały czas skanował folder i sprawdzał czy pojawiły się w nim pliki. Następnie przenieś pliki po rozszerzeniach np. pliki txt, doc, csv, pdf do folderu dokumenty, pliki: png, jpg, bmp do folderu image, pozostałe pliki do folderu różne. 
- Wykorzystując dowolny rodzaj wątku wyszukaj liczby pierwsze dla zakresu 1 000 000. Do zadania wykorzystaj 4 wątki. 
- Napisz program, który wykorzystując wielowątkowość będzie losował 1000 liczb spełniających warunek podzielności przez 3. Wszystkie liczby spełniające ten warunek dodawaj do wspólnej listy, do rozwiązania wykorzystaj dowolny rodzaj wątków. 
- Sprawdź czy jest różnica czasowa w wykonaniu zadania 3 i 4 przy wykorzystaniu różnej liczby wątków np. 1, 4 i 6. 
Last updated