synchronized Schlüsselwort in Java
Das Schlüsselwort synchronized
in Java steuert den Zugriff auf gemeinsame Ressourcen für mehrere Threads. Sie garantiert, dass immer nur ein Thread einen synchronisierten Block oder eine synchronisierte Methode ausführen kann, um Thread-Interferenzen zu verhindern und die Speicherkonsistenz zu gewährleisten.
Verwendung
Das Schlüsselwort synchronized
kann auf Methoden oder Blöcke innerhalb von Methoden angewendet werden. Wenn eine Methode oder ein Block als synchronisiert deklariert wird, erhält das angegebene Objekt eine Sperre, und andere Threads werden am Zugriff auf den synchronisierten Code gehindert, bis die Sperre freigegeben wird.
Syntax
Synchronisierte Methode
public synchronized void synchronizedMethod() {
// method code
}
Synchronisierter Block
public void method() {
synchronized (object) {
// synchronized code
}
}
object
: Das Objekt, auf das synchronisiert werden soll. Dies ist normalerweise die aktuelle Instanz (this
) oder ein bestimmtes Objekt.
Beispiele
Beispiel 1: Synchronisierte Methode
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
public static void main(String[] args) {
Counter counter = new Counter();
// Create multiple threads to increment the counter
Thread t1 = new Thread(() -> counter.increment());
Thread t2 = new Thread(() -> counter.increment());
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + counter.getCount());
}
}
In diesem Beispiel ist die Methode increment
synchronisiert, so dass immer nur ein Thread den Zähler erhöhen kann, was Race Conditions verhindert.
Beispiel 2: Synchronisierter Block
public class SynchronizedBlockExample {
private final Object lock = new Object();
private int count = 0;
public void increment() {
synchronized (lock) {
count++;
}
}
public int getCount() {
return count;
}
public static void main(String[] args) {
SynchronizedBlockExample example = new SynchronizedBlockExample();
// Create multiple threads to increment the counter
Thread t1 = new Thread(() -> example.increment());
Thread t2 = new Thread(() -> example.increment());
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final count: " + example.getCount());
}
}
In diesem Beispiel wird ein synchronisierter Block verwendet, um den Zugriff auf die Variable count
zu steuern. Das Lock-Objekt stellt sicher, dass jeweils nur ein Thread den synchronisierten Block ausführen kann.
Tipps und bewährte Praktiken
- Minimiere synchronisierten Code: Halte synchronisierte Blöcke so kurz wie möglich, um Konflikte zu vermeiden und die Leistung zu verbessern.
- Verwende Endgültige Objekte: Wenn du synchronisierte Blöcke verwendest, verwende endgültige Objekte als Sperren, um Konsistenz zu gewährleisten.
- Vermeide verschachtelte Synchronisierung: Verschachtelte synchronisierte Blöcke können zu Deadlocks führen. Vermeide sie oder verwende sie mit Vorsicht.
- Verwende Concurrent Collections: Für eine komplexe Synchronisierung solltest du nebenläufige Sammlungen aus dem Paket
java.util.concurrent
verwenden, wie z.B.ConcurrentHashMap
, die eine integrierte Synchronisierung bieten. - Flüchtig Schlüsselwort: Für Variablen, auf die mehrere Threads zugreifen, die aber keine komplexe Synchronisierung erfordern, solltest du das Schlüsselwort
volatile
verwenden, um die Sichtbarkeit sicherzustellen.
private volatile boolean flag;
- Gewindesicherheit: Stelle immer sicher, dass gemeinsam genutzte Ressourcen ordnungsgemäß synchronisiert werden, um die Threadsicherheit zu gewährleisten und Datenbeschädigungen zu vermeiden.
- Synchronisierte statische Methoden: Synchronisiere statische Methoden, um einen synchronisierten Zugriff auf Klassenebene zu erzwingen und sicherzustellen, dass nur ein Thread die statische Methode über alle Instanzen hinweg ausführen kann.
public static synchronized void staticMethod() {
// method code
}
- Wiedereintritt: Java's intrinsische Sperren sind reentrant, d.h. wenn ein Thread eine Sperre hält, kann er sie wieder erlangen, ohne sich selbst zu blockieren.