Concurrency und Multithreading in Java: Von der Theorie zur Praxis
In der modernen Softwarewelt hängt die Anwendungsleistung direkt von der Fähigkeit ab, Prozessorressourcen effizient zu nutzen. Concurrency und Multithreading in Java sind Schlüsselkonzepte, mit denen Sie mehrere Aufgaben gleichzeitig ausführen, die Datenverarbeitung beschleunigen und reaktionsfähige Benutzeroberflächen erstellen können. In diesem Artikel werden wir die grundlegenden Mechanismen des Multithreading in Java aufschlüsseln, lernen, wie man Threads erstellt und verwaltet, und uns auch mit Best Practices zur Vermeidung häufiger Fehler befassen.
Was ist ein Thread und wie erstellt man einen in Java?
Ein Thread ist ein leichtgewichtiger Prozess, der innerhalb einer einzelnen Anwendung ausgeführt wird. Jeder Thread hat seinen eigenen Aufrufstapel und kann unabhängig von anderen Threads arbeiten. In Java gibt es zwei Hauptmethoden, um einen Thread zu erstellen.
Methode 1: Vererben von der Thread-Klasse
public class MyThread extends Thread { @Override public void run() { System.out.println("Thread " + getName() + " started!"); }
public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // Starting the thread }}Methode 2: Implementieren des Runnable-Interfaces
public class MyRunnable implements Runnable { @Override public void run() { System.out.println("Thread is running via Runnable"); }
public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); thread.start(); }}Es wird empfohlen, die zweite Methode zu verwenden, da sie keine Vererbung erfordert und die gleichzeitige Implementierung mehrerer Schnittstellen ermöglicht.
Thread-Synchronisation: Vermeidung von Wettlaufsituationen
Wenn mehrere Threads gleichzeitig auf gemeinsame Daten zugreifen, tritt eine Wettlaufsituation auf. Um dies zu vermeiden, bietet Java Synchronisationsmechanismen.
Das Schlüsselwort synchronized
Blockiert den Zugriff auf eine Methode oder einen Codeblock für andere Threads, bis der aktuelle Thread die Ausführung beendet hat:
public class Counter { private int count = 0;
public synchronized void increment() { count++; }
public synchronized int getCount() { return count; }}Sperren mit Lock
Ein flexiblerer Mechanismus als synchronized. Ermöglicht das Festlegen von Zeitüberschreitungen und den Versuch, eine Sperre zu erlangen, ohne zu blockieren:
import java.util.concurrent.locks.ReentrantLock;
public class SafeCounter { private final ReentrantLock lock = new ReentrantLock(); private int count = 0;
public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); // Always release the lock } }}Thread-Pool und ExecutorService
Das Erstellen eines neuen Threads für jede Aufgabe ist ein teurer Vorgang. Um Threads effizient zu verwalten, verwenden Sie ExecutorService aus dem Paket java.util.concurrent.
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;
public class ThreadPoolExample { public static void main(String[] args) { // Create a pool of 4 threads ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) { final int taskId = i; executor.submit(() -> { System.out.println("Task " + taskId + " is executing in " + Thread.currentThread().getName()); }); }
executor.shutdown(); // Shut down the pool after all tasks are completed }}Haupttypen von Pools:
newFixedThreadPool(n)— feste Anzahl von Threads