Skip to content
IRC-Coding IRC-Coding
Multithreading Java Threads Synchronisation Wait Notify Concurrent Locks Semaphores Thread Safety

Multithreading in Java: Threads, Synchronisation, Wait/Notify & Concurrent Locks

Java Multithreading mit Threads, Synchronisation, wait/notify, concurrent locks, semaphores. Thread-Safety, Deadlock-Vermeidung und Performance-Optimierung.

S

schutzgeist

1 min read

Multithreading in Java: Threads, Synchronisation, Wait/Notify & Concurrent Locks

Dieser Beitrag ist eine umfassende Anleitung zum Java Multithreading – inklusive Threads, Synchronisation, wait/notify, concurrent locks und semaphores mit praktischen Beispielen.

In a Nutshell

Multithreading ermöglicht parallele Ausführung von Aufgaben. Synchronisation sichert konsistenten Datenzugriff, während concurrent locks moderne Mechanismen zur Steuerung von Threads bieten.

Kompakte Fachbeschreibung

Multithreading ist die Fähigkeit eines Programms, mehrere Threads gleichzeitig auszuführen. Jeder Thread hat eigenen Stack aber teilt sich den Heap-Speicher mit anderen Threads.

Grundlegende Konzepte:

Thread-Lebenszyklus

  • NEW: Thread erstellt aber noch nicht gestartet
  • RUNNABLE: Bereit zur Ausführung (running oder ready)
  • BLOCKED: Wartet auf Monitor-Lock
  • WAITING: Wartet unbegrenzt auf Bedingung
  • TIMED_WAITING: Wartet zeitlich begrenzt
  • TERMINATED: Thread beendet

Synchronisationsmechanismen

  • synchronized: Monitor-basierte Synchronisation
  • wait()/notify(): Inter-Thread-Kommunikation
  • ReentrantLock: Flexibler Lock-Mechanismus
  • ReadWriteLock: Getrennte Lese-/Schreib-Locks
  • Semaphore: Zähler-basierte Zugriffskontrolle
  • CountDownLatch: Warten auf mehrere Threads
  • CyclicBarrier: Synchronisationspunkt für Threads

Thread-Safety

  • Immutable Objects: Von Natur aus thread-safe
  • Thread-Local: Thread-spezifische Daten
  • Volatile: Sichtbarkeit von Variablen
  • Atomic Classes: Lock-freie Operationen

Prüfungsrelevante Stichpunkte

  • Threads: Leichte Prozesse mit eigenem Stack
  • Synchronisation: Schutz gemeinsamer Ressourcen
  • Monitor: Objekt-basierter Synchronisationsmechanismus
  • wait/notify: Inter-Thread-Kommunikation
  • Deadlock: Verklemmung durch gegenseitige Blockierung
  • Race Condition: Unkontrollierter Zugriff auf geteilte Daten
  • Volatile: Garantiert Sichtbarkeit zwischen Threads
  • IHK-relevant: Wichtig für performante, parallele Anwendungen

Kernkomponenten

  1. Thread-Management: Erstellung, Steuerung, Lebenszyklus
  2. Synchronisation: synchronized, locks, semaphores
  3. Inter-Thread-Kommunikation: wait/notify, blocking queues
  4. Concurrent Collections: Thread-sichere Datenstrukturen
  5. Thread Pools: ExecutorService, ThreadPoolExecutor
  6. Thread Safety: Immutable, volatile, atomic classes
  7. Performance: Lock-Granularität, contention reduction
  8. Debugging: Thread-Dumps, race conditions, deadlocks

Praxisbeispiele

1. Grundlegende Thread-Operationen

public class ThreadGrundlagen {
    
    // Thread durch Vererbung von Thread
    static class MeinThread extends Thread {
        private String name;
        
        public MeinThread(String name) {
            this.name = name;
        }
        
        @Override
        public void run() {
            for (int i = 1; i <= 5; i++) {
                System.out.println(name + " - Zählung: " + i);
                try {
                    Thread.sleep(500); // 500ms Pause
                } catch (InterruptedException e) {
                    System.out.println(name + " wurde unterbrochen");
                    return;
                }
            }
            System.out.println(name + " beendet");
        }
    }
    
    // Thread durch Implementierung von Runnable
    static class MeinRunnable implements Runnable {
        private String name;
        
        public MeinRunnable(String name) {
            this.name = name;
        }
        
        @Override
        public void run() {
            for (int i = 1; i <= 3; i++) {
                System.out.println(name + " - Arbeit: " + i);
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    System.out.println(name + " unterbrochen");
                    return;
                }
            }
            System.out.println(name + " fertig");
        }
    }
    
    // Lambda-Ausdruck als Runnable
    static void lambdaThreadDemo() {
        Thread lambdaThread = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println("Lambda Thread - Schritt " + i);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    return;
                }
            }
        });
        
        lambdaThread.start();
    }
    
    public static void main(String[] args) {
        System.out.println("=== Thread-Grundlagen ===");
        
        // Thread durch Vererbung
        MeinThread thread1 = new MeinThread("Thread-1");
        thread1.start();
        
        // Thread durch Runnable
        Thread thread2 = new Thread(new MeinRunnable("Runnable-1"));
        thread2.start();
        
        // Lambda Thread
        lambdaThreadDemo();
        
        // Auf Threads warten
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("Hauptthread beendet");
    }
}

2. Synchronisation mit synchronized

public class SynchronisationDemo {
    
    // Gemeinsame Ressource
    static class Zaehler {
        private int wert = 0;
        
        // Synchronisierte Methode
        public synchronized void erhoehen() {
            wert++;
            System.out.println(Thread.currentThread().getName() + 
                             " erhöht auf: " + wert);
        }
        
        // Synchronisierter Block
        public void verringern() {
            synchronized(this) {
                wert--;
                System.out.println(Thread.currentThread().getName() + 
                                 " verringert auf: " + wert);
            }
        }
        
        public synchronized int getWert() {
            return wert;
        }
    }
    
    // Producer-Consumer mit wait/notify
    static class Warenlager {
        private final int[] lager = new int[5];
        private int index = 0;
        
        public synchronized void einlagern(int ware) throws InterruptedException {
            // Warten wenn Lager voll
            while (index >= lager.length) {
                System.out.println("Lager voll - Producer wartet");
                wait();
            }
            
            lager[index] = ware;
            index++;
            System.out.println(Thread.currentThread().getName() + 
                             " eingelagert: " + ware);
            
            // Consumer benachrichtigen
            notifyAll();
        }
        
        public synchronized int auslagern() throws InterruptedException {
            // Warten wenn Lager leer
            while (index <= 0) {
                System.out.println("Lager leer - Consumer wartet");
                wait();
            }
            
            index--;
            int ware = lager[index];
            System.out.println(Thread.currentThread().getName() + 
                             " ausgelagert: " + ware);
            
            // Producer benachrichtigen
            notifyAll();
            
            return ware;
        }
    }
    
    static class Producer implements Runnable {
        private Warenlager lager;
        
        public Producer(Warenlager lager) {
            this.lager = lager;
        }
        
        @Override
        public void run() {
            try {
                for (int i = 1; i <= 10; i++) {
                    lager.einlagern(i);
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    static class Consumer implements Runnable {
        private Warenlager lager;
        
        public Consumer(Warenlager lager) {
            this.lager = lager;
        }
        
        @Override
        public void run() {
            try {
                for (int i = 1; i <= 10; i++) {
                    lager.auslagern();
                    Thread.sleep(150);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== Synchronisation Demo ===");
        
        // Einfache Synchronisation
        Zaehler zaehler = new Zaehler();
        
        Thread[] threads = new Thread[5];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 3; j++) {
                    zaehler.erhoehen();
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            });
            threads[i].setName("Thread-" + i);
        }
        
        // Threads starten
        for (Thread t : threads) {
            t.start();
        }
        
        // Auf Threads warten
        for (Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        System.out.println("Endwert: " + zaehler.getWert());
        
        // Producer-Consumer Demo
        System.out.println("\n=== Producer-Consumer Demo ===");
        Warenlager lager = new Warenlager();
        
        Thread producer = new Thread(new Producer(lager), "Producer");
        Thread consumer = new Thread(new Consumer(lager), "Consumer");
        
        producer.start();
        consumer.start();
        
        try {
            producer.join();
            consumer.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3. Concurrent Locks und moderne Synchronisation

import java.util.concurrent.locks.*;
import java.util.concurrent.*;

public class ConcurrentLocksDemo {
    
    // ReentrantLock Beispiel
    static class Bankkonto {
        private double kontostand;
        private final ReentrantLock lock = new ReentrantLock();
        
        public Bankkonto(double startbetrag) {
            this.kontostand = startbetrag;
        }
        
        public void einzahlen(double betrag) {
            lock.lock();
            try {
                double alterStand = kontostand;
                Thread.sleep(50); // Simuliere Verarbeitung
                kontostand = alterStand + betrag;
                System.out.println(Thread.currentThread().getName() + 
                                 " eingezahlt: " + betrag + 
                                 ", neuer Stand: " + kontostand);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        }
        
        public boolean abheben(double betrag) {
            lock.lock();
            try {
                if (kontostand >= betrag) {
                    double alterStand = kontostand;
                    Thread.sleep(50);
                    kontostand = alterStand - betrag;
                    System.out.println(Thread.currentThread().getName() + 
                                     " abgehoben: " + betrag + 
                                     ", neuer Stand: " + kontostand);
                    return true;
                } else {
                    System.out.println(Thread.currentThread().getName() + 
                                     " Konnte nicht abheben: " + betrag + 
                                     " (Stand: " + kontostand + ")");
                    return false;
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            } finally {
                lock.unlock();
            }
        }
        
        public double getKontostand() {
            lock.lock();
            try {
                return kontostand;
            } finally {
                lock.unlock();
            }
        }
    }
    
    // ReadWriteLock Beispiel
    static class ThreadSafeList {
        private final List<String> liste = new ArrayList<>();
        private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
        private final Lock readLock = rwLock.readLock();
        private final Lock writeLock = rwLock.writeLock();
        
        public void add(String element) {
            writeLock.lock();
            try {
                liste.add(element);
                System.out.println(Thread.currentThread().getName() + 
                                 " hinzugefügt: " + element);
            } finally {
                writeLock.unlock();
            }
        }
        
        public String get(int index) {
            readLock.lock();
            try {
                return liste.get(index);
            } finally {
                readLock.unlock();
            }
        }
        
        public List<String> getAll() {
            readLock.lock();
            try {
                return new ArrayList<>(liste); // Kopie zurückgeben
            } finally {
                readLock.unlock();
            }
        }
        
        public int size() {
            readLock.lock();
            try {
                return liste.size();
            } finally {
                readLock.unlock();
            }
        }
    }
    
    // Semaphore Beispiel
    static class RessourcenPool {
        private final Semaphore semaphore;
        private final List<String> ressourcen;
        
        public RessourcenPool(int maxRessourcen) {
            semaphore = new Semaphore(maxRessourcen);
            ressourcen = new ArrayList<>();
            for (int i = 1; i <= maxRessourcen; i++) {
                ressourcen.add("Ressource-" + i);
            }
        }
        
        public String acquire() throws InterruptedException {
            semaphore.acquire();
            
            synchronized(ressourcen) {
                if (!ressourcen.isEmpty()) {
                    String ressource = ressourcen.remove(0);
                    System.out.println(Thread.currentThread().getName() + 
                                     " acquired: " + ressource);
                    return ressource;
                }
            }
            
            semaphore.release();
            return null;
        }
        
        public void release(String ressource) {
            synchronized(ressourcen) {
                ressourcen.add(ressource);
                System.out.println(Thread.currentThread().getName() + 
                                 " released: " + ressource);
            }
            semaphore.release();
        }
    }
    
    // CountDownLatch Beispiel
    static class Worker implements Runnable {
        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;
        private final int workerId;
        
        public Worker(CountDownLatch startSignal, CountDownLatch doneSignal, int workerId) {
            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
            this.workerId = workerId;
        }
        
        @Override
        public void run() {
            try {
                // Warten auf Startsignal
                System.out.println("Worker " + workerId + " bereit");
                startSignal.await();
                
                // Arbeit ausführen
                System.out.println("Worker " + workerId + " arbeitet");
                Thread.sleep((long) (Math.random() * 1000));
                
                System.out.println("Worker " + workerId + " fertig");
                
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                doneSignal.countDown();
            }
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== Concurrent Locks Demo ===");
        
        // ReentrantLock Demo
        Bankkonto konto = new Bankkonto(1000.0);
        
        Thread[] bankThreads = new Thread[4];
        for (int i = 0; i < bankThreads.length; i++) {
            final int threadId = i;
            bankThreads[i] = new Thread(() -> {
                for (int j = 0; j < 3; j++) {
                    if (threadId % 2 == 0) {
                        konto.einzahlen(100);
                    } else {
                        konto.abheben(50);
                    }
                }
            }, "BankThread-" + i);
        }
        
        for (Thread t : bankThreads) {
            t.start();
        }
        
        for (Thread t : bankThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        System.out.println("Endkontostand: " + konto.getKontostand());
        
        // ReadWriteLock Demo
        System.out.println("\n=== ReadWriteLock Demo ===");
        ThreadSafeList liste = new ThreadSafeList();
        
        // Writer Thread
        Thread writer = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                liste.add("Element-" + i);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    return;
                }
            }
        }, "Writer");
        
        // Reader Threads
        Thread[] readers = new Thread[3];
        for (int i = 0; i < readers.length; i++) {
            readers[i] = new Thread(() -> {
                for (int j = 0; j < 10; j++) {
                    List<String> alle = liste.getAll();
                    System.out.println(Thread.currentThread().getName() + 
                                     " gelesen: " + alle.size() + " Elemente");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            }, "Reader-" + i);
        }
        
        writer.start();
        for (Thread reader : readers) {
            reader.start();
        }
        
        try {
            writer.join();
            for (Thread reader : readers) {
                reader.join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        // Semaphore Demo
        System.out.println("\n=== Semaphore Demo ===");
        RessourcenPool pool = new RessourcenPool(2);
        
        Thread[] poolThreads = new Thread[5];
        for (int i = 0; i < poolThreads.length; i++) {
            poolThreads[i] = new Thread(() -> {
                try {
                    String ressource = pool.acquire();
                    if (ressource != null) {
                        Thread.sleep(1000); // Ressource nutzen
                        pool.release(ressource);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }, "PoolThread-" + i);
        }
        
        for (Thread t : poolThreads) {
            t.start();
        }
        
        for (Thread t : poolThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        // CountDownLatch Demo
        System.out.println("\n=== CountDownLatch Demo ===");
        int workerCount = 3;
        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(workerCount);
        
        for (int i = 1; i <= workerCount; i++) {
            new Thread(new Worker(startSignal, doneSignal, i)).start();
        }
        
        try {
            Thread.sleep(1000);
            System.out.println("Alle Worker bereit - Startsignal!");
            startSignal.countDown();
            
            doneSignal.await();
            System.out.println("Alle Worker fertig!");
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4. Thread-Safe Datenstrukturen und Atomic Classes

import java.util.concurrent.atomic.*;
import java.util.concurrent.*;

public class ThreadSafeCollections {
    
    // Atomic Classes Demo
    static class AtomicZaehler {
        private final AtomicInteger zaehler = new AtomicInteger(0);
        private final AtomicLong longZaehler = new AtomicLong(0);
        private final AtomicBoolean flag = new AtomicBoolean(false);
        private final AtomicReference<String> nachricht = new AtomicReference<>("");
        
        public void increment() {
            int alterWert = zaehler.getAndIncrement();
            System.out.println(Thread.currentThread().getName() + 
                             " increment: " + alterWert + " -> " + zaehler.get());
        }
        
        public void add(long wert) {
            long alterWert = longZaehler.getAndAdd(wert);
            System.out.println(Thread.currentThread().getName() + 
                             " add: " + alterWert + " + " + wert + " -> " + longZaehler.get());
        }
        
        public void toggleFlag() {
            boolean alterWert = flag.getAndSet(!flag.get());
            System.out.println(Thread.currentThread().getName() + 
                             " toggle: " + alterWert + " -> " + flag.get());
        }
        
        public void updateNachricht(String neueNachricht) {
            String alteNachricht = nachricht.getAndSet(neueNachricht);
            System.out.println(Thread.currentThread().getName() + 
                             " update: '" + alteNachricht + "' -> '" + neueNachricht + "'");
        }
        
        public int getZaehler() { return zaehler.get(); }
        public long getLongZaehler() { return longZaehler.get(); }
        public boolean getFlag() { return flag.get(); }
        public String getNachricht() { return nachricht.get(); }
    }
    
    // Concurrent Collections Demo
    static class ConcurrentCollectionsDemo {
        
        public static void hashMapDemo() {
            System.out.println("=== ConcurrentHashMap Demo ===");
            ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
            
            // Writer Threads
            Thread[] writers = new Thread[3];
            for (int i = 0; i < writers.length; i++) {
                final int threadId = i;
                writers[i] = new Thread(() -> {
                    for (int j = 0; j < 5; j++) {
                        String key = "Key-" + threadId + "-" + j;
                        map.put(key, threadId * 100 + j);
                        System.out.println(Thread.currentThread().getName() + 
                                         " put: " + key);
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            return;
                        }
                    }
                }, "Writer-" + i);
            }
            
            // Reader Thread
            Thread reader = new Thread(() -> {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName() + 
                                     " size: " + map.size());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            }, "Reader");
            
            // Alle Threads starten
            reader.start();
            for (Thread writer : writers) {
                writer.start();
            }
            
            // Auf Threads warten
            try {
                for (Thread writer : writers) {
                    writer.join();
                }
                reader.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println("Final map size: " + map.size());
        }
        
        public static void blockingQueueDemo() {
            System.out.println("\n=== BlockingQueue Demo ===");
            BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);
            
            // Producer
            Thread producer = new Thread(() -> {
                try {
                    for (int i = 1; i <= 10; i++) {
                        String item = "Item-" + i;
                        queue.put(item);
                        System.out.println("Producer put: " + item + 
                                         " (queue size: " + queue.size() + ")");
                        Thread.sleep(200);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }, "Producer");
            
            // Consumer
            Thread consumer = new Thread(() -> {
                try {
                    for (int i = 1; i <= 10; i++) {
                        String item = queue.take();
                        System.out.println("Consumer take: " + item + 
                                         " (queue size: " + queue.size() + ")");
                        Thread.sleep(300);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }, "Consumer");
            
            producer.start();
            consumer.start();
            
            try {
                producer.join();
                consumer.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    // ThreadLocal Demo
    static class ThreadLocalDemo {
        private static ThreadLocal<Integer> threadLocalValue = ThreadLocal.withInitial(() -> 100);
        private static ThreadLocal<String> threadLocalName = new ThreadLocal<>();
        
        public static void demo() {
            System.out.println("=== ThreadLocal Demo ===");
            
            Thread[] threads = new Thread[3];
            for (int i = 0; i < threads.length; i++) {
                final int threadId = i;
                threads[i] = new Thread(() -> {
                    threadLocalName.set("Thread-" + threadId);
                    
                    for (int j = 0; j < 3; j++) {
                        int wert = threadLocalValue.get();
                        String name = threadLocalName.get();
                        
                        System.out.println(name + " wert: " + wert);
                        
                        // ThreadLocal Wert ändern
                        threadLocalValue.set(wert + threadId);
                        
                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            return;
                        }
                    }
                    
                    // ThreadLocal aufräumen
                    threadLocalName.remove();
                    threadLocalValue.remove();
                }, "Thread-" + i);
            }
            
            for (Thread t : threads) {
                t.start();
            }
            
            for (Thread t : threads) {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public static void main(String[] args) {
        // Atomic Classes Demo
        System.out.println("=== Atomic Classes Demo ===");
        AtomicZaehler zaehler = new AtomicZaehler();
        
        Thread[] atomicThreads = new Thread[4];
        for (int i = 0; i < atomicThreads.length; i++) {
            final int threadId = i;
            atomicThreads[i] = new Thread(() -> {
                zaehler.increment();
                zaehler.add(threadId * 10);
                zaehler.toggleFlag();
                zaehler.updateNachricht("Nachricht von Thread-" + threadId);
            }, "AtomicThread-" + i);
        }
        
        for (Thread t : atomicThreads) {
            t.start();
        }
        
        for (Thread t : atomicThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        System.out.println("Final values:");
        System.out.println("Zaehler: " + zaehler.getZaehler());
        System.out.println("LongZaehler: " + zaehler.getLongZaehler());
        System.out.println("Flag: " + zaehler.getFlag());
        System.out.println("Nachricht: " + zaehler.getNachricht());
        
        // Concurrent Collections Demo
        ConcurrentCollectionsDemo.hashMapDemo();
        ConcurrentCollectionsDemo.blockingQueueDemo();
        
        // ThreadLocal Demo
        ThreadLocalDemo.demo();
    }
}

Thread-Pools und ExecutorService

ThreadPoolExecutor

// Fixed Thread Pool
ExecutorService fixedPool = Executors.newFixedThreadPool(4);

// Cached Thread Pool
ExecutorService cachedPool = Executors.newCachedThreadPool();

// Single Thread Executor
ExecutorService singlePool = Executors.newSingleThreadExecutor();

// Scheduled Thread Pool
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);

// Tasks ausführen
Future<String> future = fixedPool.submit(() -> {
    Thread.sleep(1000);
    return "Ergebnis";
});

// Scheduled Tasks
scheduledPool.scheduleAtFixedRate(() -> {
    System.out.println("Periodische Aufgabe");
}, 0, 1, TimeUnit.SECONDS);

// Pool herunterfahren
fixedPool.shutdown();
scheduledPool.shutdown();

Deadlock-Vermeidung

Deadlock-Bedingungen

  1. Mutual Exclusion: Ressource kann nur von einem Thread genutzt werden
  2. Hold and Wait: Thread hält Ressourcen und wartet auf weitere
  3. No Preemption: Ressourcen können nicht erzwungen freigegeben werden
  4. Circular Wait: Zirkuläre Warteschleife zwischen Threads

Vermeidungsstrategien

// Lock Ordering - immer in gleicher Reihenfolge locken
public void transfer(Account from, Account to, double amount) {
    // Synchronisiere Accounts in konsistenter Reihenfolge
    Account first = from.getId() < to.getId() ? from : to;
    Account second = from.getId() < to.getId() ? to : from;
    
    synchronized(first) {
        synchronized(second) {
            from.withdraw(amount);
            to.deposit(amount);
        }
    }
}

// TryLock mit Timeout
public boolean transferWithTryLock(Account from, Account to, double amount) {
    while (true) {
        try {
            if (from.getLock().tryLock(1, TimeUnit.SECONDS)) {
                try {
                    if (to.getLock().tryLock(1, TimeUnit.SECONDS)) {
                        try {
                            from.withdraw(amount);
                            to.deposit(amount);
                            return true;
                        } finally {
                            to.getLock().unlock();
                        }
                    }
                } finally {
                    from.getLock().unlock();
                }
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
        // Kurze Pause vor erneutem Versuch
        Thread.sleep(100);
    }
}

Performance-Optimierung

Lock-Granularität

// Grobkörnige Synchronisation (schlecht)
public synchronized void addElement(Object element) {
    // Ganze Operation gesperrt
    list.add(element);
    size++;
}

// Feinkörnige Synchronisation (besser)
public void addElement(Object element) {
    synchronized(list) {
        list.add(element);
    }
    synchronized(this) {
        size++;
    }
}

Volatile vs Synchronized

// Volatile für einfache Sichtbarkeit
private volatile boolean running = true;

public void stop() {
    running = false; // Sichtbar für alle Threads
}

public void run() {
    while (running) {
        // Arbeit ausführen
    }
}

// Synchronized für komplexe Operationen
private int counter = 0;

public synchronized void increment() {
    counter++; // Atomare Operation
}

Vorteile und Nachteile

Vorteile von Multithreading

  • Performance: Parallele Ausführung auf Multi-Core-Systemen
  • Responsiveness: UI bleibt während langer Operationen reaktiv
  • Ressourcennutzung: Bessere Auslastung von Systemressourcen
  • Skalierbarkeit: Aufgaben können verteilt werden

Nachteile

  • Komplexität: Synchronisation ist fehleranfällig
  • Debugging: Race Conditions sind schwer zu reproduzieren
  • Overhead: Thread-Erstellung und -Kontextwechsel kosten Zeit
  • Ressourcen: Mehr Speicher und CPU-Verbrauch

Häufige Prüfungsfragen

  1. Was ist der Unterschied zwischen wait() und sleep()? wait() gibt Lock frei, sleep() behält Lock. wait() benötigt synchronized, sleep() nicht.

  2. Erklären Sie Deadlock und wie man ihn vermeidet! Deadlock ist gegenseitige Blockierung. Vermeidung durch Lock Ordering, TryLock mit Timeout.

  3. Wann verwendet man volatile statt synchronized? volatile für einfache Sichtbarkeit von Variablen, synchronized für komplexe Operationen.

  4. Was ist der Unterschied zwischen Runnable und Callable? Runnable hat keine Rückgabe, Callable gibt Ergebnis zurück und kann Exceptions werfen.

Wichtigste Quellen

  1. https://docs.oracle.com/javase/tutorial/essential/concurrency/
  2. https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html
  3. https://www.baeldung.com/java-concurrency
Zurück zum Blog
Share: