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
- Thread-Management: Erstellung, Steuerung, Lebenszyklus
- Synchronisation: synchronized, locks, semaphores
- Inter-Thread-Kommunikation: wait/notify, blocking queues
- Concurrent Collections: Thread-sichere Datenstrukturen
- Thread Pools: ExecutorService, ThreadPoolExecutor
- Thread Safety: Immutable, volatile, atomic classes
- Performance: Lock-Granularität, contention reduction
- 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
- Mutual Exclusion: Ressource kann nur von einem Thread genutzt werden
- Hold and Wait: Thread hält Ressourcen und wartet auf weitere
- No Preemption: Ressourcen können nicht erzwungen freigegeben werden
- 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
-
Was ist der Unterschied zwischen wait() und sleep()? wait() gibt Lock frei, sleep() behält Lock. wait() benötigt synchronized, sleep() nicht.
-
Erklären Sie Deadlock und wie man ihn vermeidet! Deadlock ist gegenseitige Blockierung. Vermeidung durch Lock Ordering, TryLock mit Timeout.
-
Wann verwendet man volatile statt synchronized? volatile für einfache Sichtbarkeit von Variablen, synchronized für komplexe Operationen.
-
Was ist der Unterschied zwischen Runnable und Callable? Runnable hat keine Rückgabe, Callable gibt Ergebnis zurück und kann Exceptions werfen.
Wichtigste Quellen
- https://docs.oracle.com/javase/tutorial/essential/concurrency/
- https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/package-summary.html
- https://www.baeldung.com/java-concurrency