→ Slide 1

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

→ Slide 2

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

→ Slide 3

Proces vs wątek

→ Slide 4

Gdzie współbieżność ma sens

→ Slide 5

Wątki w Javie - dwa podstawowe podejścia

→ Slide 6

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();
→ Slide 7

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();
→ Slide 8

Metody klasy Thread

Dokumentacja: Thread (Java SE 17 & JDK 17)

→ Slide 9

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/)

→ Slide 10

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
→ Slide 11

Przestarzałe metody Thread

→ Slide 12

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();
            }
        }
    }
}
→ Slide 13

Monitor w Javie

→ Slide 14

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;
        }
    }
}
→ Slide 15

Komunikacja między wątkami

→ Slide 16

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;
    }
}
→ Slide 17

Priorytety wątków

→ Slide 18

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

→ Slide 19

Problem: race condition (wyścig danych)

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

Rozwiązania wyścigu danych

→ Slide 21

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();
        }
    }
}
→ Slide 22

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();
}
→ Slide 23

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();
→ Slide 24

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();
→ Slide 25

Fork/JoinPool

→ Slide 26

Deadlock, starvation, livelock

→ Slide 27

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

→ Slide 28

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:

→ Slide 29

Ćwiczenia praktyczne

→ Slide 30

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,…:

→ Slide 31

Materiały dodatkowe