Spis treści

REVEAL theme=simple&disableLayout=0&transition=none&controls=1&show_progress_bar=1&build_all_lists=0&show_image_borders=0&horizontal_slide_level=2&enlarge_vertical_slide_headers=0&show_slide_details=1&open_in_new_window=1&size=1024×768

<html> <style type=„text/css”> p { text-align: left;} .reveal { font-size: 30px; } .reveal section { width: 96%; margin: 0 auto; } .reveal .slides { width: 60% !important; } .reveal ul, .reveal ol { margin-left: 1.5em; } .reveal pre code {

height: 100%;
max-height: 100%;
box-sizing: border-box; }

</style> </html>

Współbieżność w języku Java

Wielozadaniowość a współbieżność

Proces vs wątek

Gdzie współbieżność ma sens

Wątki w Javie - dwa podstawowe podejścia

Przykład: Thread

class CounterThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(getName() + " -> " + i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return;
            }
        }
    }
}
 
// użycie
CounterThread t1 = new CounterThread();
t1.start();

Przykład: Runnable

class PrintTask implements Runnable {
    private final String label;
 
    PrintTask(String label) {
        this.label = label;
    }
 
    @Override
    public void run() {
        System.out.println("Start: " + label + ", thread=" + Thread.currentThread().getName());
    }
}
 
// użycie
Thread t = new Thread(new PrintTask("Import danych"));
t.start();

Metody klasy Thread

Dokumentacja: Thread (Java SE 17 & JDK 17)

Cykl życia i stany wątku

Cykl życia wątku (źródło: https://howtodoinjava.com/java/multi-threading/java-thread-life-cycle-and-thread-states/)

Przykład

Thread worker = new Thread(() -> {
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});
 
worker.start();                         // uruchomienie wątku
System.out.println(worker.isAlive());   // zwykle true
worker.join();                          // czeka aż worker zakończy
System.out.println(worker.isAlive());   // false

Przestarzałe metody Thread

Poprawny wzorzec zatrzymywania wątku

class Worker implements Runnable {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            // porcja pracy
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

Monitor w Javie

synchronized - metoda i blok

class SafeCounter {
    private int value;
 
    // metoda synchronizowana na całym obiekcie
    public synchronized void inc() {
        value++;
    }
 
    public int get() {
        // blok synchronizowany na tym obiekcie
        synchronized (this) {
            return value;
        }
    }
}

Komunikacja między wątkami

Producent-konsument (monitor)

class OneSlotBuffer {
    private Integer value = null;
 
    public synchronized void put(int v) throws InterruptedException {
        while (value != null) {
            wait();      // czeka aż konsument pobierze wartość
        }
        value = v;
        notifyAll();
    }
 
    public synchronized int take() throws InterruptedException {
        while (value == null) {
            wait();      // czeka aż producent wstawi wartość
        }
        int result = value;
        value = null;
        notifyAll();
        return result;
    }
}

Priorytety wątków

Framework współbieżności: java.util.concurrent

Problem: race condition (wyścig danych)

class BrokenCounter {
    int value = 0;
    void inc() { value++; }
}

Rozwiązania wyścigu danych

Obiekty Lock (ReentrantLock)

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
class LockCounter {
    private final Lock lock = new ReentrantLock();
    private int value = 0;
 
    void inc() {
        lock.lock();
        try {
            value++;
        } finally {
            lock.unlock();
        }
    }
}

Semafory

import java.util.concurrent.Semaphore;
 
Semaphore semaphore = new Semaphore(3); // max 3 równolegle
semaphore.acquire();
try {
    // sekcja z ograniczonym dostępem
} finally {
    semaphore.release();
}

Narzędzia wysokiego poziomu: ExecutorService

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> System.out.println("zadanie A"));
pool.shutdown();

Callable i Future

import java.util.concurrent.*;
 
ExecutorService pool = Executors.newSingleThreadExecutor();
Future<Integer> f = pool.submit(() -> 40 + 2);
Integer result = f.get(); // może blokować
pool.shutdown();

Fork/JoinPool

Deadlock, starvation, livelock

Jak unikać problemów współbieżności

Częste wyjątki i pułapki

Nieprzechwycony i nieobsłużony wyjątek spowoduje przejście wątku do stanu TERMINATED

Wyjątki, które mogą wystąpić w kodzie współbieżnym:

Ćwiczenia praktyczne

Zadanie 5: Liczby pierwsze

Napisz program, który wyznacza liczbę liczb pierwszych w zakresie od 2 do N wielowątkowo. Program sporządza porównanie szybkości działania w zależności od liczby wątków i pokazuje przyspieszenie względem wersji jednowątkowej.

Wymagania:

Program powinien wypisać, najlepiej w formie tabeli, dla każdej liczby wątków K=1,2,4,…:

Materiały dodatkowe