Sobes.tech
Middle

Mi az a synchronized blokk Java-ban, és milyen problémák merülhetnek fel a használata során?

sobes.tech MI

Válasz az MI-től

A Java synchronized blokkja a közös erőforrásokhoz való hozzáférés szinkronizálására szolgál több szál között. Ez garantálja, hogy csak egy szál hajthat végre kódot a blokkban egy adott időben.

A synchronized blokk szerkezete:

synchronized (objektum_monitor) {
    // Kritikus szekció
    // Itt történnek a közös erőforrással kapcsolatos műveletek
}
  • objektum_monitor: bármilyen objektum, amely monitor (zár) szerepét tölti be. Azok a szálak, amelyek ugyanazzal az objektummal próbálnak belépni a synchronized blokkba, blokkolva lesznek, amíg a jelenlegi szál ki nem lép a blokkból.

A synchronized használata során felmerülő problémák:

  • Halálos zárlat (Deadlock): Két vagy több szál vár egy erőforrás felszabadítására, amelyet egy másik szál foglal el, ez a program lefagyásához vezet. Ez akkor fordulhat elő, ha a szálak különböző sorrendben próbálják megszerezni a zárolásokat.
  • Teljesítménycsökkenés: A szinkronizáció blokkolja a többi szál végrehajtását, amelyek a monitorhoz való hozzáférést várják, ez lassíthatja a párhuzamos alkalmazásokat.
  • Nem alkalmazható bizonyos szcenáriókban: A synchronized kizárólagos hozzáférést biztosít. Bizonyos esetekben rugalmasabb hozzáférés-vezérlésre lehet szükség (pl. több szál olvashat egyszerre, de csak egy írhat).
  • Hibakeresés nehézségei: A szinkronizációs problémák nem mindig nyilvánvalóak, és nehéz reprodukálni vagy hibakeresni őket.

Példa a halálos zárlatra:

class Resource {
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void method1(Resource other) {
        synchronized (lock1) {
            try {
                Thread.sleep(100); // Munka szimulálása
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " megszerezte a lock1-et");
            synchronized (other.lock2) {
                System.out.println(Thread.currentThread().getName() + " megszerezte a másik lock2-ét");
            }
        }
    }

    public void method2(Resource other) {
        synchronized (lock2) {
             try {
                Thread.sleep(100); // Munka szimulálása
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " megszerezte a lock2-t");
            synchronized (other.lock1) {
                System.out.println(Thread.currentThread().getName() + " megszerezte a másik lock1-et");
            }
        }
    }
}

public class DeadlockExample {
    public static void main(String[] args) {
        Resource r1 = new Resource();
        Resource r2 = new Resource();

        new Thread(() -> r1.method1(r2), "Thread-1").start();
        new Thread(() -> r2.method2(r1), "Thread-2").start();
    }
}

Ebben a példában az Thread-1 megpróbálja megszerezni az lock1-et r1-en, majd az lock2-t r2-n. A Thread-2 megpróbálja megszerezni az lock2-t r2-n, majd az lock1-t r1-en. Ha mindkettő egyszerre szerzi meg az első zárolásokat, akkor halálos zárlat alakul ki.