Demo 1 (20-25 min): Wyścig danych i poprawki

Writing /home/grochu/public_html/wiki/data/cache/5/5d1dc3cdfc3d5e9164d1f0abd21bc510.i failed
Unable to save cache file. Hint: disk full; file permissions; safe_mode setting.
Writing /home/grochu/public_html/wiki/data/cache/5/5d1dc3cdfc3d5e9164d1f0abd21bc510.metadata failed
Action unknown: siteexport_addpage
Writing /home/grochu/public_html/wiki/data/cache/a/a78cbd33a6c694b3f51b5258479b5b5a.i failed
Writing /home/grochu/public_html/wiki/data/meta/zajecia/java/sidebar.meta failed
Unable to save metadata file. Hint: disk full; file permissions; safe_mode setting.
Writing /home/grochu/public_html/wiki/data/cache/5/5d1dc3cdfc3d5e9164d1f0abd21bc510.i failed
Unable to save cache file. Hint: disk full; file permissions; safe_mode setting.

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

Rozwiązanie (minimum):

// podziel [0, n) na k przedziałów
// submit Callable<Long>
// 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.