๐Ÿ“š [Java] - synchronized

CodeByHanยท2025๋…„ 11์›” 8์ผ

์ž๋ฐ”

๋ชฉ๋ก ๋ณด๊ธฐ
10/13

์ž๋ฐ” ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“ค๋‹ค ๋ณด๋ฉด, ์˜๋„์น˜ ์•Š์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค. ์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ ์ž์ฃผ ๋“ฑ์žฅํ•˜๋Š” ํ‚ค์›Œ๋“œ๊ฐ€ ์žˆ๋‹ค. ๋ฐ”๋กœ synchronized๋‹ค. ์˜ค๋Š˜์€ synchronized๋ฅผ ์กฐ๊ธˆ ๋” ๊นŠ๊ฒŒ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

์ž๋ฐ”์˜ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ

synchronized๋ฅผ ์ดํ•ดํ•˜๋ ค๋ฉด ๋จผ์ € ์ž๋ฐ”์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์•„๋Š” ๊ฒƒ์ด ๋„์›€์ด ๋œ๋‹ค.

์ž๋ฐ” ๋ฉ”๋ชจ๋ฆฌ๋Š” ํฌ๊ฒŒ Stack, Heap, Method Area๋กœ ๊ตฌ๋ถ„๋œ๋‹ค. ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ๋Š” ์Šค๋ ˆ๋“œ๋“ค์ด Heap๊ณผ Method Area๋ฅผ ๊ณต์œ ํ•˜์ง€๋งŒ, Stack์€ ๊ฐ ์Šค๋ ˆ๋“œ๋งˆ๋‹ค ๊ณ ์œ ํ•˜๊ฒŒ ์กด์žฌํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณต์œ  ์ž์›์— ์ ‘๊ทผํ•  ๋•Œ๋Š” ๋™๊ธฐํ™” ๋ฌธ์ œ๋ฅผ ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.

์ด๋Ÿฌํ•œ ๋™๊ธฐํ™” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์ฃผ๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ synchronized ํ‚ค์›Œ๋“œ๋‹ค.

๋ฉ”์„œ๋“œ ์˜์—ญ: ์ „์—ญ ๋ณ€์ˆ˜ , static ๋ณ€์ˆ˜ ์ €์žฅ, ํ”„๋กœ๊ทธ๋žจ ์‹œ์ž‘๋ถ€ํ„ฐ ์ข…๋ฃŒ๊นŒ์ง€ ๋ฉ”๋ชจ๋ฆฌ์— ๋‚จ์•„์žˆ๋‹ค.
์Šคํƒ ์˜์—ญ: ์ง€์—ญ ๋ณ€์ˆ˜, ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ฐ์ดํ„ฐ ๊ฐ’์ด ์ €์žฅ๋˜๋Š” ๊ณต๊ฐ„, ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ๋ฉ”๋ชจ๋ฆฌ์— ํ• ๋‹น ๋˜๊ณ  ์ข…๋ฃŒ๋˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•ด์ œ ๋œ๋‹ค.
ํž™ ์˜์—ญ: new ํ‚ค์›Œ๋“œ๋กœ ์ƒ์„ฑ๋˜๋Š” ๊ฐ์ฒด(์ธ์Šคํ„ด์Šค), ๋ฐฐ์—ด ๋“ฑ์ด ์ €์žฅ๋˜๋ฉฐ, ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์— ์˜ํ•ด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๊ด€๋ฆฌ๋œ๋‹ค.

synchronized method

public static void main(String[] args) {

    SyncDemo demo = new SyncDemo();
    Thread tA = new Thread(() -> {
        System.out.println("Thread-A ์‹œ์ž‘: " + LocalDateTime.now());
        demo.firstMethod("Thread-A");
        System.out.println("Thread-A ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    Thread tB = new Thread(() -> {
        System.out.println("Thread-B ์‹œ์ž‘: " + LocalDateTime.now());
        demo.secondMethod("Thread-B");
        System.out.println("Thread-B ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    tA.start();
    tB.start();
}

private synchronized void firstMethod(String name) {
    System.out.println(name + " -> firstMethod ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private synchronized void secondMethod(String name) {
    System.out.println(name + " -> secondMethod ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

SyncDemo ์ธ์Šคํ„ด์Šค๋ฅผ ํ•œ๊ฐœ ์ƒ์„ฑํ•˜๊ณ , ๋‘ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค์–ด ๊ฐ๊ฐ synchronized ํ‚ค์›Œ๋“œ๊ฐ€ ๋ถ™์€ ThreadA, ThreadB ๋ฅผ ํ˜ธ์ถœํ•ด๋ณด์•˜๋‹ค.

ํ™•์ธ ๊ฒฐ๊ณผ, ThreadA ํ˜ธ์ถœ -> ์ข…๋ฃŒ ๋œ ํ›„, ๋‹ค์Œ ThreadB ๊ฐ€ ํ˜ธ์ถœ -> ์ข…๋ฃŒ ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋งŒ์•ฝ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ๊ฐ ๋งŒ๋“ค๊ณ , ์Šค๋ ˆ๋“œ๋“ค์„ ํ˜ธ์ถœํ•˜๊ฒŒ ํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

public static void main(String[] args) {

    SyncDemo demo1 = new SyncDemo();
    SyncDemo demo2 = new SyncDemo();

    Thread tA = new Thread(() -> {
        System.out.println("Thread-A ์‹œ์ž‘: " + LocalDateTime.now());
        demo1.firstMethod("Thread-A");
        System.out.println("Thread-A ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    Thread tB = new Thread(() -> {
        System.out.println("Thread-B ์‹œ์ž‘: " + LocalDateTime.now());
        demo2.secondMethod("Thread-B");
        System.out.println("Thread-B ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    tA.start();
    tB.start();
}

private synchronized void firstMethod(String name) {
    System.out.println(name + " -> firstMethod ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private synchronized void secondMethod(String name) {
    System.out.println(name + " -> secondMethod ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

์ด ๊ฒฝ์šฐ, ๊ฐ ์Šค๋ ˆ๋“œ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐ์ฒด์˜ lock์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์Šค๋ ˆ๋“œ ๊ฐ„ ๋™๊ธฐํ™”๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ฒฐ๊ณผ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋“ฏ์ด, synchronized ๋ฉ”์„œ๋“œ๋Š” ํ•ด๋‹น ์ธ์Šคํ„ด์Šค์— lock์„ ๊ฑด๋‹ค.

์—ฌ๊ธฐ์„œ โ€œ์ธ์Šคํ„ด์Šค์— lock์„ ๊ฑด๋‹คโ€๋Š” ํ‘œํ˜„ ๋•Œ๋ฌธ์—, ๋งˆ์น˜ ์ธ์Šคํ„ด์Šค ์ž์ฒด์— ์ ‘๊ทผ์ด ๋ถˆ๊ฐ€๋Šฅํ•ด์ง€๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์˜คํ•ดํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ์—๋งŒ lock์ด ์ ์šฉ๋œ๋‹ค.

ํ•œ๋ฒˆ ์ฝ”๋“œ๋กœ ํ™•์ธํ•ด๋ณด์ž!!

public static void main(String[] args) {

    SyncDemo demo = new SyncDemo(); // ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋งŒ ์ƒ์„ฑ

    Thread tA = new Thread(() -> {
        System.out.println("Thread-A ์‹œ์ž‘: " + LocalDateTime.now());
        demo.firstMethod("Thread-A");
        System.out.println("Thread-A ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    Thread tB = new Thread(() -> {
        System.out.println("Thread-B ์‹œ์ž‘: " + LocalDateTime.now());
        demo.secondMethod("Thread-B");
        System.out.println("Thread-B ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    Thread tC = new Thread(() -> {
        System.out.println("Thread-C ์‹œ์ž‘: " + LocalDateTime.now());
        demo.thirdMethod("Thread-C");
        System.out.println("Thread-C ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    tA.start();
    tB.start();
    tC.start();
}

private synchronized void firstMethod(String name) {
    System.out.println(name + " -> firstMethod ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private synchronized void secondMethod(String name) {
    System.out.println(name + " -> secondMethod ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private void thirdMethod(String name) {
    System.out.println(name + " -> thirdMethod ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

์ด ์ฝ”๋“œ์—์„œ Thread-C๋Š” thirdMethod๋ฅผ ์‹คํ–‰ํ•˜๋Š”๋ฐ, ์ด ๋ฉ”์„œ๋“œ๋Š” synchronized๊ฐ€ ๋ถ™์ง€ ์•Š์€ ์ผ๋ฐ˜ ๋ฉ”์„œ๋“œ๋‹ค.
๋”ฐ๋ผ์„œ Thread-C์—๋Š” ๋ฝ์ด ๊ฑธ๋ฆฌ์ง€ ์•Š๊ณ , ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ(firstMethod, secondMethod)์™€ ๋™์‹œ์— ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด, synchronized ๋ฉ”์„œ๋“œ๋Š” ๊ฐ์ฒด(์ธ์Šคํ„ด์Šค) ๋‹จ์œ„๋กœ ๋ฝ์„ ๊ฑด๋‹ค.

ํ•˜์ง€๋งŒ ๋ฝ์€ synchronized ํ‚ค์›Œ๋“œ๊ฐ€ ๋ถ™์€ ๋ฉ”์„œ๋“œ๋ผ๋ฆฌ๋งŒ ๊ณต์œ ๋˜๋ฏ€๋กœ, ์ผ๋ฐ˜ ๋ฉ”์„œ๋“œ์—๋Š” ๋ฝ์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.

๊ฒฐ๋ก 

  • synchronized ํ‚ค์›Œ๋“œ๊ฐ€ ๋ถ™์€ ๋ฉ”์„œ๋“œ๋Š” ํ•ด๋‹น ๊ฐ์ฒด์— ๋ฝ์„ ๊ฑธ์–ด ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    ์ฆ‰, ๊ฐ™์€ ๊ฐ์ฒด์˜ synchronized ๋ฉ”์„œ๋“œ๋Š” ๋™์‹œ์— ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐ์ฒด์˜ synchronized ๋ฉ”์„œ๋“œ๋Š” ๊ฐ๊ฐ ๋ณ„๋„์˜ ๋ฝ์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋™์‹œ์— ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋‹ค.

  • ๋”ฐ๋ผ์„œ, synchronized ๋ฉ”์„œ๋“œ ๋™๊ธฐํ™”๋Š” ๊ฐ์ฒด ๋‹จ์œ„๋ผ๋Š” ์ ์ด ์ค‘์š”ํ•˜๋‹ค.

  • ๋™๊ธฐํ™” ๋ฒ”์œ„๋Š” synchronized ๋ฉ”์„œ๋“œ์— ํ•œ์ •

  • ์ผ๋ฐ˜ ๋ฉ”์„œ๋“œ๋Š” ๋ฝ๊ณผ ์ƒ๊ด€์—†์ด ์–ธ์ œ๋“  ๋™์‹œ์— ์‹คํ–‰ ๊ฐ€๋Šฅํ•˜๋‹ค.

  • ๋ฝ์€ synchronized๊ฐ€ ๋ถ™์€ ๋ฉ”์„œ๋“œ๋ผ๋ฆฌ๋งŒ ๊ณต์œ ๋œ๋‹ค.

static synchronized method

public static void main(String[] args) {
    Thread tA = new Thread(() -> {
        System.out.println("Thread-A ์‹œ์ž‘: " + LocalDateTime.now());
        syncMethodA("Thread-A");
        System.out.println("Thread-A ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    Thread tB = new Thread(() -> {
        System.out.println("Thread-B ์‹œ์ž‘: " + LocalDateTime.now());
        syncMethodB("Thread-B");
        System.out.println("Thread-B ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    tA.start();
    tB.start();
}

public static synchronized void syncMethodA(String name) {
    System.out.println(name + " -> syncMethodA ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public static synchronized void syncMethodB(String name) {
    System.out.println(name + " -> syncMethodB ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด, static ํ‚ค์›Œ๋“œ๊ฐ€ ๋ถ™์€
synchronized ๋ฉ”์„œ๋“œ๋Š” ์ธ์Šคํ„ด์Šค๊ฐ€ ์•„๋‹Œ ํด๋ž˜์Šค ๋‹จ์œ„๋กœ lock์„ ๊ณต์œ ํ•œ๋‹ค.

๋”ฐ๋ผ์„œ static synchronized๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์—ฌ๋Ÿฌ ๊ฐ์ฒด๊ฐ€ ์กด์žฌํ•ด๋„ ํด๋ž˜์Šค ๋‹จ์œ„๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ๋™๊ธฐํ™”๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

์—ฌ๊ธฐ๋‹ค๊ฐ€ ์ผ๋ฐ˜ synchronized ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

public static void main(String[] args) {
    StaticLockDemo demo = new StaticLockDemo();

    Thread t1 = new Thread(() -> {
        System.out.println("Thread-1 ์‹œ์ž‘: " + LocalDateTime.now());
        syncStaticA("Thread-1");
        System.out.println("Thread-1 ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    Thread t2 = new Thread(() -> {
        System.out.println("Thread-2 ์‹œ์ž‘: " + LocalDateTime.now());
        syncStaticB("Thread-2");
        System.out.println("Thread-2 ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    Thread t3 = new Thread(() -> {
        System.out.println("Thread-3 ์‹œ์ž‘: " + LocalDateTime.now());
        demo.syncC("Thread-3");
        System.out.println("Thread-3 ์ข…๋ฃŒ: " + LocalDateTime.now());
    });

    t1.start();
    t2.start();
    t3.start();
}

public static synchronized void syncStaticA(String name) {
    System.out.println(name + " -> syncStaticA ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public static synchronized void syncStaticB(String name) {
    System.out.println(name + " -> syncStaticB ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private synchronized void syncC(String name) {
    System.out.println(name + " -> syncC ์‹คํ–‰ ์ค‘: " + LocalDateTime.now());
    try {
        TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

syncStaticA ๊ณผ syncStaticB๋Š” ๋™๊ธฐํ™”๊ฐ€ ์ž˜ ์ง€์ผœ์ง€๋Š”๊ฑธ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ผ๋ฐ˜ synchronized์ธ syncC๋Š” ์ง€์ผœ์ง€์ง€ ์•Š๋Š”๊ฑธ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํด๋ž˜์Šค ๋‹จ์œ„๋กœ ๋ฝ์„ ๊ฑฐ๋Š” static synchronized์™€ ์ธ์Šคํ„ด์Šค ๋‹จ์œ„๋กœ ๋ฝ์„ ๊ฑฐ๋Š” ์ผ๋ฐ˜ synchronized๋ฅผ ํ˜ผ์šฉํ•  ๊ฒฝ์šฐ, ์–ด๋А ๋ฝ์ด ์ ์šฉ๋˜๋Š”์ง€ ํ—ท๊ฐˆ๋ฆด ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ฃผ์˜ํ•˜์ž.

synchronized block

synchronized block์€ ๋ฉ”์„œ๋“œ ์ „์ฒด๊ฐ€ ์•„๋‹Œ, ์ฝ”๋“œ ๋ธ”๋ก ๋‹จ์œ„๋กœ ๋™๊ธฐํ™”๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

synchronized(this)

public static void main(String[] args) {

    SyncBlockDemo demo = new SyncBlockDemo();

    Thread t1 = new Thread(() -> {
        System.out.println("Thread-A ์‹œ์ž‘ " + LocalDateTime.now());
        demo.lockBlockA("Thread-A");
        System.out.println("Thread-A ์ข…๋ฃŒ " + LocalDateTime.now());
    });

    Thread t2 = new Thread(() -> {
        System.out.println("Thread-B ์‹œ์ž‘ " + LocalDateTime.now());
        demo.lockBlockB("Thread-B");
        System.out.println("Thread-B ์ข…๋ฃŒ " + LocalDateTime.now());
    });

    t1.start();
    t2.start();
}

private void lockBlockA(String name) {
    synchronized (this) {
        System.out.println(name + " -> lockBlockA ์‹คํ–‰ ์ค‘ " + LocalDateTime.now());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

private void lockBlockB(String name) {
    synchronized (this) {
        System.out.println(name + " -> lockBlockB ์‹คํ–‰ ์ค‘ " + LocalDateTime.now());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

์œ„์™€ ๊ฐ™์ด synchronized ๋ธ”๋ก์—์„œ this๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ๋ชจ๋“  ํ•ด๋‹น ๊ฐ์ฒด์˜ synchronized ๋ธ”๋ก์— ๊ฐ™์€ ๋ฝ์ด ๊ฑธ๋ฆฌ๊ฒŒ ๋œ๋‹ค.

์ฆ‰, ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ synchronized ๋ธ”๋ก์„ ํ˜ธ์ถœํ•˜๋”๋ผ๋„, this์— ๋ฝ์ด ๊ฑธ๋ ค ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰ํ•ด์•ผ ํ•œ๋‹ค.

synchronized(Object)

public class LockBlockDemo {

    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public static void main(String[] args) {

        LockBlockDemo demo = new LockBlockDemo();

        Thread t1 = new Thread(() -> {
            System.out.println("Thread-1 ์‹œ์ž‘ " + LocalDateTime.now());
            demo.blockA("Thread-1");
            System.out.println("Thread-1 ์ข…๋ฃŒ " + LocalDateTime.now());
        });

        Thread t2 = new Thread(() -> {
            System.out.println("Thread-2 ์‹œ์ž‘ " + LocalDateTime.now());
            demo.blockB("Thread-2");
            System.out.println("Thread-2 ์ข…๋ฃŒ " + LocalDateTime.now());
        });

        t1.start();
        t2.start();
    }

    private void blockA(String name) {
        synchronized (lockA) {
            System.out.println(name + " -> blockA ์‹คํ–‰ ์ค‘ " + LocalDateTime.now());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void blockB(String name) {
        synchronized (lockB) {
            System.out.println(name + " -> blockB ์‹คํ–‰ ์ค‘ " + LocalDateTime.now());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Thread-1๊ณผ Thread-2๊ฐ€ ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ๋ณด๋ฉด, ๋™๊ธฐํ™”๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์€ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

this ๋Œ€์‹  lockA, lockB์™€ ๊ฐ™์€ ๋ณ„๋„์˜ ๊ฐ์ฒด๋ฅผ ๋ฝ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด, ๋™์‹œ์— ๋ฝ์„ ๊ฑธ์–ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

์‹ฑ๊ธ€ํ†ค(Singleton) ๊ฐ์ฒด์˜ ๋™๊ธฐํ™”

public class SimpleSingleton {

    private static SimpleSingleton sSimpleSingleton;

    public static SimpleSingleton getInstance() {
        if (Objects.isNull(sSimpleSingleton)) {
            sSimpleSingleton = new SimpleSingleton();
        }
        return sSimpleSingleton;
    }
}

์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์งœ๋ดค๋‹ค.

ํ•˜์ง€๋งŒ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— getInstance()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋™์‹œ ์ ‘๊ทผ ๋ฌธ์ œ(race condition) ๋ฐœ์ƒ ๊ฐ€๋Šฅํ•˜๋‹ค.

์—ฌ๊ธฐ์„œ ์ด์ œ ๋‘๊ฐ€์ง€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์กด์žฌํ•œ๋‹ค.

๋ฐ”๋กœ ๋ฉ”์„œ๋“œ์— synchronized ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์ด๊ฑฐ๋‚˜, Double-Checked Locking (DCL) , LazyHolder๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

์ผ๋‹จ synchronized ํ‚ค์›Œ๋“œ ๋ฅผ ๋ถ™์—ฌ๋ณด์ž

public static synchronized SimpleSingleton getInstance() {
    if (Objects.isNull(sSimpleSingleton)) {
        sSimpleSingleton = new SimpleSingleton();
    }
    return sSimpleSingleton;
}

ํ•˜์ง€๋งŒ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ด๋ฏธ ์ƒ์„ฑ๋œ ์ดํ›„์—๋„, ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ synchronized๋ฅผ ๊ฑฐ์ณ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

ํŠนํžˆ getInstance()๋ฅผ ์ž์ฃผ ํ˜ธ์ถœํ•˜๋Š” ์ƒํ™ฉ์—์„œ๋Š”, ๋ถˆํ•„์š”ํ•œ ๋ฝ ๋น„์šฉ์ด ๋ฐ˜๋ณต์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.

Double-Checked Locking (DCL)

public class DCLSingleton {

    // volatile ํ‚ค์›Œ๋“œ๋กœ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ
    private static volatile DCLSingleton instance;

    private DCLSingleton() {}

    public static DCLSingleton getInstance() {
        if (instance == null) {                  // 1์ฐจ ์ฒดํฌ (๋ฝ ๊ฑธ๊ธฐ ์ „)
            synchronized (DCLSingleton.class) {  // ํด๋ž˜์Šค ๋‹จ์œ„๋กœ ๋ฝ
                if (instance == null) {          // 2์ฐจ ์ฒดํฌ (๋ฝ ์•ˆ์—์„œ)
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}

Double-Checked Locking(DCL)์€ ๋ฉ”์„œ๋“œ ์ „์ฒด์— synchronized๋ฅผ ๊ฑธ์ง€ ์•Š๊ณ , ๋™๊ธฐํ™” ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด ์„ค๊ณ„๋œ ๋ฐฉ์‹์ด๋‹ค.

์ตœ์ดˆ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋œ ์ดํ›„์—๋Š” ๋™๊ธฐํ™” ๋ธ”๋ก์— ์ง„์ž…ํ•˜์ง€ ์•Š์•„ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™” ์‹œ๋„

ํ•˜์ง€๋งŒ ์ฃผ์˜ํ•  ์ ์ด ์žˆ๋‹ค.

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ Thread A๊ฐ€ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋„์ค‘ Thread B๊ฐ€ ์ด๋ฏธ ํ• ๋‹น๋œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด ๊ฒฝ์šฐ, ์ธ์Šคํ„ด์Šค๊ฐ€ ์™„์ „ํžˆ ์ดˆ๊ธฐํ™”๋˜๊ธฐ ์ „์— ์‚ฌ์šฉ๋˜๋ฏ€๋กœ ์˜ค๋™์ž‘์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ™•๋ฅ ์€ ๋‚ฎ์ง€๋งŒ, ์•ˆ์ „์„ฑ์„ ์œ„ํ•ด ์ ์ ˆํ•œ ์ฒ˜๋ฆฌ(์˜ˆ: volatile ์„ ์–ธ) ์—†์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค.

LazyHolder(์ถ”์ฒœ)

public class LazyHolderSingleton {


    private LazyHolderSingleton() {
    }

    // ์ค‘์ฒฉ(static) ํด๋ž˜์Šค์— ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    private static class Holder {
        private static final LazyHolderSingleton INSTANCE = new LazyHolderSingleton();
    }

    // ์™ธ๋ถ€์—์„œ ํ˜ธ์ถœ ์‹œ Holder ํด๋ž˜์Šค๊ฐ€ ๋กœ๋”ฉ๋˜๋ฉด์„œ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    public static LazyHolderSingleton getInstance() {
        return Holder.INSTANCE;
    }
}

๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๋™๊ธฐํ™” ๋ฌธ์ œ๋ฅผ ์ฝ”๋“œ๋กœ ์ฒ˜๋ฆฌํ•˜๋ ค๊ณ  ํ•˜๋ฉด, ํ”„๋กœ๊ทธ๋žจ ๊ตฌ์กฐ๊ฐ€ ๋ณต์žกํ•ด์ง€๊ณ , ์„ฑ๋Šฅ ๋น„์šฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํŠนํžˆ ์ •ํ™•ํ•˜๊ฒŒ ๊ตฌํ˜„ํ•˜๊ธฐ ์–ด๋ ค์šด ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

LazyHolder ๋ฐฉ์‹์€ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ”ผํ•˜๊ณ , JVM์˜ ํด๋ž˜์Šค ์ดˆ๊ธฐํ™” ๊ณผ์ •์—์„œ ๋ณด์žฅ๋˜๋Š” ์›์ž์  ํŠน์„ฑ์„ ์ด์šฉํ•ด ์‹ฑ๊ธ€ํ†ค ์ดˆ๊ธฐํ™” ์ฑ…์ž„์„ JVM์—๊ฒŒ ๋งก๊ธฐ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

Singleton ํด๋ž˜์Šค ์ž์ฒด์—๋Š” LazyHolder ํด๋ž˜์Šค์˜ ๋ณ€์ˆ˜๊ฐ€ ์—†์Œ โ†’ Singleton ํด๋ž˜์Šค ๋กœ๋”ฉ ์‹œ์ ์—์„œ๋Š” Holder ํด๋ž˜์Šค๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์Œ

getInstance() ๋ฉ”์„œ๋“œ์—์„œ Holder.INSTANCE๋ฅผ ์ฐธ์กฐํ•˜๋Š” ์ˆœ๊ฐ„, Holder ํด๋ž˜์Šค๊ฐ€ ๋กœ๋”ฉ๋˜๊ณ  ์ธ์Šคํ„ด์Šค ์ดˆ๊ธฐํ™”๊ฐ€ ์ง„ํ–‰๋จ

ํด๋ž˜์Šค ๋กœ๋”ฉ๊ณผ ์ดˆ๊ธฐํ™” ๊ณผ์ •์€ JVM์— ์˜ํ•ด ๋™๊ธฐํ™”๊ฐ€ ๋ณด์žฅ๋˜๋ฏ€๋กœ, volatile์ด๋‚˜ synchronized ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์„ฑ๋Šฅ๋„ ์šฐ์ˆ˜ํ•˜๋‹ค.


๐Ÿงน ์ •๋ฆฌ

1. ์™œ ์ž๋ฐ”์—์„œ ๋™๊ธฐํ™” ๋ฌธ์ œ๋ฅผ ์‹ ๊ฒฝ ์จ์•ผ ํ• ๊นŒ?

์ž๋ฐ”์—์„œ ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์ด ๋˜๋ฉด, ์Šค๋ ˆ๋“œ๋“ค์ด ํž™ ์˜์—ญ๊ณผ ๋ฉ”์„œ๋“œ(method) ์˜์—ญ์„ ๊ณต์œ ํ•˜๊ฒŒ ๋œ๋‹ค.

์ฆ‰, ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์“ธ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ณต์œ  ์ž์›์— ๋Œ€ํ•œ ๋™๊ธฐํ™”๋ฅผ ๋ฐ˜๋“œ์‹œ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.

2. synchronized ํ‚ค์›Œ๋“œ๋ž€?

synchronized๋Š” ๋ฝ(lock)์„ ์‚ฌ์šฉํ•ด ๋™์‹œ ์ ‘๊ทผ์„ ์ œ์–ดํ•˜๋Š” ํ‚ค์›Œ๋“œ๋‹ค.

์ฃผ์š” ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์€ ๋„ค ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

1. synchronized method

  • ๋ฉ”์„œ๋“œ ์„ ์–ธ๋ถ€์— synchronized๋ฅผ ๋ถ™์ด๋ฉด, ๋™์ผ ์ธ์Šคํ„ด์Šค์˜ ๋ฉ”์„œ๋“œ๋ผ๋ฆฌ๋งŒ lock์„ ๊ณต์œ ํ•œ๋‹ค.

  • ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค์—์„œ๋Š” ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์œผ๋ฏ€๋กœ, ์ธ์Šคํ„ด์Šค๋ณ„๋กœ ๋…๋ฆฝ์ ์ธ ๋ฝ์ด ๊ฑธ๋ฆฐ๋‹ค.

2. static synchronized method

  • ํด๋ž˜์Šค ๋‹จ์œ„๋กœ lock์ด ๊ฑธ๋ฆฌ๋ฉฐ, ์ธ์Šคํ„ด์Šค ๋ฝ๊ณผ๋Š” ๋ณ„๋„๋กœ ๋™์ž‘ํ•œ๋‹ค.

  • ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ ๊ฐ„์˜ ๋™๊ธฐํ™”๋Š” ๋ณด์žฅ๋˜์ง€๋งŒ, ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ์™€๋Š” lock์ด ๊ณต์œ ๋˜์ง€ ์•Š๋Š”๋‹ค.

3. synchronized block

  • ํ•„์š”ํ•œ ์ฝ”๋“œ ๋ธ”๋ก๋งŒ ๋ฝ์œผ๋กœ ๊ฐ์‹ธ์„œ ๋™๊ธฐํ™”๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋ฐฉ๋ฒ• 1: this๋ฅผ ์‚ฌ์šฉ โ†’ ํ˜„์žฌ ๊ฐ์ฒด๋ฅผ ๋ฝ์œผ๋กœ ์‚ฌ์šฉ. ๋‹ค๋ฅธ block๋„ ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋Œ€๊ธฐํ•ด์•ผ ํ•จ

  • ๋ฐฉ๋ฒ• 2: ๋ณ„๋„ Object๋ฅผ ๋ฝ์œผ๋กœ ์‚ฌ์šฉ โ†’ ๋ธ”๋ก๋ณ„๋กœ ๋…๋ฆฝ์ ์ธ lock์„ ๊ฑธ ์ˆ˜ ์žˆ์–ด ํšจ์œจ์ 

4. static synchronized block

  • static synchronized method์ฒ˜๋Ÿผ ํด๋ž˜์Šค ๋‹จ์œ„๋กœ lock์„ ๊ณต์œ 

  • ๋‹ค๋งŒ block ๋‹จ์œ„๋กœ lock ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด, ์„ธ๋ฐ€ํ•œ ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค

3. Singleton ๊ฐ์ฒด์—์„œ synchronized๋ฅผ ์“ฐ๋ฉด ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ

synchronized ๋ฉ”์„œ๋“œ๊ฐ€ ๋งŽ์œผ๋ฉด, ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ๋„ ๋ณ‘๋ชฉ ํ˜„์ƒ์ด ๋ฐœ์ƒ

์‰ฝ๊ฒŒ ๋งํ•ด, ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜์ง€๋งŒ ์‚ฌ์‹ค์ƒ ์‹ฑ๊ธ€ ์Šค๋ ˆ๋“œ์ฒ˜๋Ÿผ ๋А๋ ค์ง

๐Ÿ“„ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•

-> LazyHolder ํŒจํ„ด์„ ์‚ฌ์šฉ

JVM์˜ ํด๋ž˜์Šค ์ดˆ๊ธฐํ™” ์‹œ์ ์—์„œ ๋ณด์žฅ๋˜๋Š” thread-safe ํŠน์„ฑ์„ ์ด์šฉํ•ด ์ดˆ๊ธฐํ™”

๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ synchronized ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋จ โ†’ ๋ณต์žก์„ฑ ๊ฐ์†Œ

Holder ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋Š” getInstance() ํ˜ธ์ถœ ์‹œ์ ์—๋งŒ ์ƒ์„ฑ โ†’ ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ๋„ ์ข‹์Œ

์ฐธ๊ณ 

Java์˜ ๋™๊ธฐํ™” Synchronized ๊ฐœ๋… ์ •๋ฆฌ#2
์ถœ์ฒ˜: https://tourspace.tistory.com/55?category=788398 [ํˆฌ๋œ์ด์˜ ๋ฆฌ์–ผ ๋ธ”๋กœ๊ทธ:ํ‹ฐ์Šคํ† ๋ฆฌ]

[์ฝ๊ณ ์„œ] ์ž๋ฐ” ๊ณ ์œ ๋ฝ๊ณผ Synchronization

[Java] ํ˜ผ๋™๋˜๋Š” synchronized ๋™๊ธฐํ™” ์ •๋ฆฌ

profile
๋…ธ๋ ฅ์€ ๋ฐฐ์‹ ํ•˜์ง€ ์•Š์•„ ๐Ÿ”ฅ

0๊ฐœ์˜ ๋Œ“๊ธ€