Middle
Czym jest blok synchronized w Java i jakie problemy mogą wystąpić podczas jego używania?
sobes.tech AI
Odpowiedź od AI
blok synchronized w Javie jest używany do zapewnienia synchronizacji dostępu do wspólnego zasobu między wieloma wątkami. Gwarantuje to, że tylko jeden wątek może wykonywać kod wewnątrz bloku w danym momencie.
Struktura bloku synchronized:
synchronized (obiekt_monitor) {
// Sekcja krytyczna kodu
// Operacje na wspólnym zasobie
}
obiekt_monitor: dowolny obiekt, który działa jako monitor (lock). Wątki próbujące wejść do blokusynchronizedz tym samym obiektem monitora będą blokowane do momentu opuszczenia bloku przez bieżący wątek.
Problemy, które mogą wystąpić przy użyciu synchronized:
- Deadlock (zakleszczenie): Dwa lub więcej wątków oczekuje na zwolnienie zasobów, które są zajęte przez inne wątki, co prowadzi do zatrzymania programu. Może się to zdarzyć, jeśli wątki próbują uzyskać blokady w różnych kolejnościach.
- Obniżenie wydajności: Synchronizacja blokuje wykonanie innych wątków oczekujących na dostęp do monitora, co może spowolnić działanie aplikacji równoległych.
- Nie w każdym scenariuszu:
synchronizedzapewnia wyłączny dostęp. W niektórych przypadkach może być potrzebne bardziej elastyczne zarządzanie dostępem (np. odczyt wielu wątków jednocześnie, zapis w jednym wątku). - Złożoność debugowania: Problemy z synchronizacją mogą być nieoczywiste i trudne do odtworzenia i debugowania.
Przykład zakleszczenia:
class Resource {
private Object lock1 = new Object();
private Object lock2 = new Object();
public void method1(Resource other) {
synchronized (lock1) {
try {
Thread.sleep(100); // Symulacja pracy
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " uzyskał lock1");
synchronized (other.lock2) {
System.out.println(Thread.currentThread().getName() + " uzyskał lock2 innego zasobu");
}
}
}
public void method2(Resource other) {
synchronized (lock2) {
try {
Thread.sleep(100); // Symulacja pracy
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " uzyskał lock2");
synchronized (other.lock1) {
System.out.println(Thread.currentThread().getName() + " uzyskał lock1 innego zasobu");
}
}
}
}
public class DeadlockExample {
public static void main(String[] args) {
Resource r1 = new Resource();
Resource r2 = new Resource();
new Thread(() -> r1.method1(r2), "Wątek-1").start();
new Thread(() -> r2.method2(r1), "Wątek-2").start();
}
}
W tym przykładzie, Wątek-1 próbuje uzyskać lock1 na r1, a następnie lock2 na r2. Wątek-2 próbuje uzyskać lock2 na r2, a następnie lock1 na r1. Jeśli oba uzyskają swoje pierwsze blokady jednocześnie, dojdzie do zakleszczenia.