===== Demo 1 (20-25 min): Wyścig danych i poprawki =====
**Cel**: pokazać, że brak synchronizacji daje błędne wyniki.
**Kroki**:
* Utwórz licznik bez synchronizacji.
* Uruchom 4-8 wątków, każdy wykonuje np. 100_000 inkrementacji.
* Porównaj wynik oczekiwany i rzeczywisty.
* Napraw wersją ''synchronized''.
* Napraw wersją ''AtomicInteger'' i porównaj czytelność.
**Szkielet**:
int threads = 8;
int increments = 100_000;
// uruchomienie wielu wątków i join
// wypisanie: expected = threads * increments
===== Demo 1 - wskazówki i rozwiązanie =====
**Wskazówki**:
* Zadbaj o ''join()'' dla wszystkich wątków.
* Ten sam obiekt licznika musi być współdzielony.
**Wersja poprawna (AtomicInteger)**:
import java.util.concurrent.atomic.AtomicInteger;
class AtomicCounter {
private final AtomicInteger value = new AtomicInteger();
void inc() { value.incrementAndGet(); }
int get() { return value.get(); }
}
===== Demo 2 (20-25 min): Producent-konsument =====
**Cel**: komunikacja ''wait/notifyAll'' na monitorze.
**Zakres**:
* Bufor 1-elementowy.
* Producent tworzy liczby 1..N.
* Konsument odczytuje i wypisuje.
* Bez aktywnego oczekiwania (bez pętli z ''sleep'' jako mechanizmu synchronizacji).
**Wskazówki**:
* ''put()'' i ''take()'' jako ''synchronized''.
* Warunki pilnuj przez ''while''.
===== Demo 2 - rozwiązanie (skrót) =====
OneSlotBuffer buffer = new OneSlotBuffer();
Thread producer = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try { buffer.put(i); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
});
Thread consumer = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try { System.out.println(buffer.take()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
});
producer.start();
consumer.start();
===== Zadania samodzielne (20-25 min) - wybór =====
Wybierz **jedno** zadanie. Każde zawiera wersję podstawową i rozszerzenie.
===== Zadanie A (łatwe): Licznik wielowątkowy =====
**Wymagania**:
* Zaimplementuj ''Counter'' i uruchom 6 wątków po 50_000 inkrementacji.
* Pokaż wersję błędną (bez synchronizacji) oraz poprawną.
* Dodaj pomiar czasu ''System.nanoTime()''.
**Wskazówki**:
* Użyj ''join()''.
* W wersji poprawnej zastosuj ''AtomicInteger'' albo ''synchronized''.
**Rozwiązanie (minimum)**:
// expected = 300_000
// brokenCounter -> zwykle mniej
// fixedCounter -> dokładnie 300_000
===== Zadanie B (średnie): Suma tablicy w wielu wątkach =====
**Wymagania**:
* Wygeneruj tablicę ''int[]''.
* Policz sumę jednowątkowo i wielowątkowo (podział na fragmenty).
* Porównaj wyniki i czas.
**Wskazówki**:
* Każdy wątek liczy lokalną sumę fragmentu.
* Wyniki zsumuj po ''join()''.
* Alternatywa: ''ExecutorService'' + ''Callable''.
**Rozwiązanie (minimum)**:
// podziel [0, n) na k przedziałów
// submit Callable
// sumuj wyniki z Future.get()
===== Zadanie C (trudniejsze): Ograniczony downloader =====
**Wymagania**:
* Symuluj pobieranie wielu plików (''Thread.sleep(...)'').
* Maksymalnie 3 pobrania równocześnie.
* Wypisuj start/koniec zadania i nazwę wątku.
**Wskazówki**:
* Użyj ''Semaphore(3)'' albo ''Executors.newFixedThreadPool(3)''.
* Pamiętaj o zwalnianiu zasobu w ''finally''.
**Rozwiązanie (minimum)**:
ExecutorService pool = Executors.newFixedThreadPool(3);
for (String file : files) {
pool.submit(() -> {
System.out.println("START " + file + " " + Thread.currentThread().getName());
Thread.sleep(500);
System.out.println("END " + file + " " + Thread.currentThread().getName());
});
}
pool.shutdown();
===== Zadanie D (dla chętnych): Deadlock i naprawa =====
**Wymagania**:
* Napisz przykład deadlocka na 2 blokadach.
* Następnie napraw go przez stałą kolejność blokad.
**Wskazówki**:
* Dwa obiekty: ''lockA'', ''lockB''.
* Wersja błędna: różna kolejność w dwóch wątkach.
* Wersja poprawna: zawsze najpierw ''lockA'', potem ''lockB''.
===== Zadanie PA =====
==== Zadanie 4.1 ====
Zaimplementuj klasę, która wykorzystując wątki pozwoli na podanie sumy wszystkich elementów w
tablicach wygenerowanych losowo. Użytkownik ma mieć możliwość określenia długości tablic i ich
liczby.
==== Zadanie 4.2 ====
Zaimplementuj klasę statyczną, która z wykorzystaniem wielu wątków pozwoli wykonywanie
mnożenia dwóch macierzy przechowywanych w postaci tablic dwuwymiarowych. Zadbaj o to, aby
przed rozpoczęciem operacji sprawdzić, czy mnożenie macierzy można w ogóle wykonać.
==== Zadanie 4.3 ====
Zaimplementuj klasę statyczną, która pozwoli z wykorzystaniem wielu wątków doprowadzić macierz
przechowywaną w postaci tablicy dwuwymiarowej do postaci górnotrójkątnej (eliminacja Gaussa).
Dodatkowe 3 pkt. otrzymać można za doprowadzenie macierzy do postaci zdiagonalizowanej. Przed
wykonaniem operacji sprawdź, czy spełnione są warunki niezbędne do tego, aby operacja się
powiodła.