===== 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.