๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Java

Java์˜ ๋™๊ธฐํ™” ๊ธฐ๋ฒ• - synchronized & ReentrantLock ์— ๋Œ€ํ•ด

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ˆœ์œผ๋กœ ๊ธ€์ด ์ด์–ด์ง‘๋‹ˆ๋‹ค.

synchronized ์†Œ๊ฐœ
synchronized vs static synchronized 
synchronized ๋‹จ์ ์„ ๊ทน๋ณตํ•œ ReentrantLock ์†Œ๊ฐœ
synchronized vs ReentrantLock

 

1. synchronized ์†Œ๊ฐœ

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ๋Š” ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ์ž์›์— ์ ‘๊ทผํ•  ๊ฒฝ์šฐ, ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฌธ์ œ(๋ฐ์ดํ„ฐ ์†์ƒ, ์ถฉ๋Œ ๋“ฑ)๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋™๊ธฐํ™”(synchronization) ๊ธฐ๋ฒ•์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

Java์—์„œ ์ œ๊ณตํ•˜๋Š” synchronized ํ‚ค์›Œ๋“œ๋Š” ๋ชจ๋‹ˆํ„ฐ ๋ฝ(Monitor Lock)์„ ์ด์šฉํ•ด ๋™๊ธฐํ™”๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

 

๋ชจ๋‹ˆํ„ฐ๋Š” OS๊ฐ€ ์•„๋‹Œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด ์ˆ˜์ค€์—์„œ ์ œ๊ณตํ•˜๋Š” ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. Java์˜ ๋ชจ๋“  ๊ฐ์ฒด(์ธ์Šคํ„ด์Šค)๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ๊ณ ์œ ํ•œ ๋ชจ๋‹ˆํ„ฐ๋ฅผ ๊ฐ€์ง€๋ฉฐ, ์ด๋ฅผ ํš๋“ํ•˜๊ณ  ํ•ด์ œํ•˜๋ฉฐ ๋™๊ธฐํ™” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋ชจ๋‹ˆํ„ฐ์™€ ๋ฝ์€ ์„œ๋กœ ์ƒํ˜ธ๋ณด์™„์ ์ธ ๊ด€๊ณ„๋กœ ๋Œ€์ฒดํ•˜๊ฑฐ๋‚˜ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋…์ด ์•„๋‹™๋‹ˆ๋‹ค.

- ์ถœ์ฒ˜ : In Java, what is the difference between a monitor and a lock

 

synchronized ํŠน์ง•

  • ์Šค๋ ˆ๋“œ๊ฐ€ synchronized ํ‚ค์›Œ๋“œ๊ฐ€ ๋ถ™์€ ๋ฉ”์„œ๋“œ์— ์ง„์ž…ํ•˜๋ ค๋ฉด ํ•ด๋‹น ๊ฐ์ฒด์˜ ๋ฝ์„ ํš๋“ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฝ์„ ํš๋“ํ•˜์ง€ ๋ชปํ•œ ์Šค๋ ˆ๋“œ๋Š” RUNNABLE ์ƒํƒœ์—์„œ BLOCKED ์ƒํƒœ๋กœ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค. ๋ฝ์„ ํš๋“ํ•  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•˜๋ฉฐ, ์ด ๋™์•ˆ CPU ์‹คํ–‰ ์Šค์ผ€์ค„๋ง์—์„œ ์ œ์™ธ๋ฉ๋‹ˆ๋‹ค.
  • ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋Œ€๊ธฐ ์ค‘์ผ ๊ฒฝ์šฐ, ๋ฝ ํš๋“ ์ˆœ์„œ๋Š” ๋ณด์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • synchronized ๋ธ”๋ก ์•ˆ์—์„œ๋Š” ๋ณ€์ˆ˜์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ฐ€์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ์ž๋™์œผ๋กœ ํ•ด๊ฒฐ๋˜๋ฏ€๋กœ, ๋ณ„๋„์˜ volatile ์„ ์–ธ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

synchronized ๋‹จ์  2๊ฐ€์ง€

  • ๋ฌดํ•œ ๋Œ€๊ธฐ : BLOCKED ์ƒํƒœ์˜ ์Šค๋ ˆ๋“œ๋Š” ๋ฝ์ด ํ’€๋ฆด ๋•Œ๊นŒ์ง€ ๋ฌดํ•œ ๋Œ€๊ธฐ๋กœ ์ธํ„ฐ๋ŸฝํŠธ, ํƒ€์ž„์•„์›ƒ ์ ์šฉ์ด ์•ˆ๋ฉ๋‹ˆ๋‹ค.
  • ๊ณต์ •์„ฑ ๋ฌธ์ œ: ๋ฝ์ด ๋Œ์•„์™”์„ ๋•Œ, BLOCKED ์ƒํƒœ์˜ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ ์ค‘์— ์–ด๋–ค ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ํš๋“ํ•  ์ง€ ์•Œ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

BLOCKED ๋Š” ์Šค๋ ˆ๋“œ์˜ ์ƒํƒœ ์ค‘ ์ผ๋ถ€๋กœ ์Šค๋ ˆ๋“œ์˜ ์ƒํƒœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

NEW ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ์ง€๋งŒ ์•„์ง ์‹œ์ž‘๋˜์ง€ ์•Š์€ ์ƒํƒœ. Thread thread = new Thread(runnable)
RUNNABLE ์‹คํ–‰ ์ค‘์ด๊ฑฐ๋‚˜ ์‹คํ–‰ ์ค€๋น„๊ฐ€ ์™„๋ฃŒ๋œ ์ƒํƒœ. thread.start()
BLOCKED ๋™๊ธฐํ™” ๋ฝ์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ƒํƒœ. (synchronized ์‚ฌ์šฉ ์‹œ ๋ฐœ์ƒ) synchronized (lock) {…}
WAITING ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ ํŠน์ • ์ž‘์—…์ด ์™„๋ฃŒ๋˜๊ธฐ๋ฅผ ๋ฌดํ•œ์ • ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ƒํƒœ. (wait(), join() ํ˜ธ์ถœ ์‹œ)
TIMED_WAITING ํŠน์ • ์‹œ๊ฐ„ ๋™์•ˆ ๋Œ€๊ธฐํ•˜๋Š” ์ƒํƒœ. (sleep(), wait(timeout), join(timeout) ํ˜ธ์ถœ ์‹œ)
TERMINATED ์Šค๋ ˆ๋“œ์˜ ์‹คํ–‰์ด ์™„๋ฃŒ๋œ ์ƒํƒœ.

 

BLOCKED & WAITING ์€ ๋ชจ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๋Œ€๊ธฐํ•˜๋ฉฐ CPU ์‹คํ–‰ ์Šค์ผ€์ค„๋ง์— ๋“ค์–ด๊ฐ€์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, CPU์—์„œ ๋ณด๋ฉด ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š” ๋น„์Šทํ•œ ์ƒํƒœ์ด์ง€๋งŒ ํฐ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

 

BLOCKED → ์ธํ„ฐ๋ŸฝํŠธ๊ฐ€ ๊ฑธ๋ ค๋„ ์—ฌ์ „ํžˆ BLOCKED

  • synchronized ์—์„œ ๋ฝ์„ ํš๋“ํ•˜๊ธฐ ์œ„ํ•ด ๋Œ€๊ธฐํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํŠน๋ณ„ํ•œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

WAITING(TIME_WAITING) → ์ธํ„ฐ๋ŸฝํŠธ๊ฐ€ ๊ฑธ๋ฆฌ๋ฉด RUNNABLE ๋ณ€๊ฒฝ

  • ์Šค๋ ˆ๋“œ๊ฐ€ ํŠน์ • ์กฐ๊ฑด์ด๋‚˜ ์‹œ๊ฐ„ ๋™์•ˆ ๋Œ€๊ธฐํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

 

2. synchronized vs static synchronized

synchronized ํ‚ค์›Œ๋“œ๊ฐ€ ์ธ์Šคํ„ด์Šค๋ณ„๋กœ ์ž‘๋™ํ•˜๋Š”์ง€, static synchronized๊ฐ€ ํด๋ž˜์Šค ๋ ˆ๋ฒจ์—์„œ ์ž‘๋™ํ•˜๋Š”์ง€ ์‹คํ—˜์„ ํ†ตํ•ด ํ™•์ธํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์‹คํ—˜ 1, synchronized ๋ฉ”์„œ๋“œ์˜ ๋™์ž‘ ํ™•์ธ

์‹คํ—˜ ์ฝ”๋“œ

public class SyncMain {

    public static void main(String[] args) {
        MyTask task1 = new MyTask();

        Thread thread1 = new Thread(task1, "thread1");
        thread1.start();

        Thread thread2 = new Thread(task1, "thread2");
        thread2.start();
    }

    static class MyTask implements Runnable {

        Logger logger = new Logger();

        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            while (true) {
                logger.logging(name);
                sleep(1000);
            }
        }
    }
}

public class Logger {

    public synchronized void logging(String threadName) {
        while (true) {
            System.out.println("logging: " + threadName);
            sleep(1000);
        }
    }
}

์‹คํ—˜ ๋ชฉ์ : ๋™์ผ ์ธ์Šคํ„ด์Šค์˜ synchronized ๋ฉ”์„œ๋“œ๋ฅผ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ํ˜ธ์ถœํ•  ๊ฒฝ์šฐ ๋™๊ธฐํ™”๊ฐ€ ์ด๋ฃจ์–ด์ง€๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ƒ ๊ฒฐ๊ณผ: ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ logging ๋ฉ”์„œ๋“œ(์ž„๊ณ„์˜์—ญ)์— ์ง„์ž…ํ•˜๋ฉด, ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋Š” BLOCKED ์ƒํƒœ๊ฐ€ ๋˜์–ด ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ: ํ•œ ์Šค๋ ˆ๋“œ์˜ ๋กœ๊ทธ๋งŒ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ๋™๊ธฐํ™”๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ด๋ฃจ์–ด์กŒ์Œ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์‹คํ—˜ 2, synchronized์˜ ์ธ์Šคํ„ด์Šค๋ณ„ ๋ฝ ํ™•์ธ

์‹คํ—˜ ์ฝ”๋“œ

MyTask task1 = new MyTask();
MyTask task2 = new MyTask();

Thread thread1 = new Thread(task1, "thread1");
thread1.start();

Thread thread2 = new Thread(task2, "thread2");
thread2.start();

์‹คํ—˜ ๋ชฉ์ : ๊ฐ๊ธฐ ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค๊ฐ€ ์„œ๋กœ ๋…๋ฆฝ์ ์ธ ๋ชจ๋‹ˆํ„ฐ ๋ฝ์„ ๊ฐ€์ง€๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ƒ ๊ฒฐ๊ณผ

  • ๊ฐ ์ธ์Šคํ„ด์Šค๋Š” ์ž์‹ ๋งŒ์˜ ๋ชจ๋‹ˆํ„ฐ ๋ฝ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋‘ ์Šค๋ ˆ๋“œ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค์˜ logging ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฏ€๋กœ ๊ฐ๊ฐ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ: ์Šค๋ ˆ๋“œ๋ณ„๋กœ ๋กœ๊ทธ๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ณ ์œ ํ•œ ๋ชจ๋‹ˆํ„ฐ ๋ฝ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

์‹คํ—˜ 3, static synchronized์˜ ํด๋ž˜์Šค ๋ ˆ๋ฒจ ๋ฝ ํ™•์ธ

์‹คํ—˜ ์ฝ”๋“œ

public class SyncMain {

    public static void main(String[] args) {
        MyTask task1 = new MyTask();
        MyTask task2 = new MyTask();

        Thread thread1 = new Thread(task1, "thread1");
        thread1.start();

        Thread thread2 = new Thread(task2, "thread2");
        thread2.start();
    }

    static class MyTask implements Runnable {
        
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            while (true) {
                Logger.logging(name);
                sleep(1000);
            }
        }
    }
}

public class Logger {

    public static synchronized void logging(String threadName) {
        while (true) {
            System.out.println("logging: " + threadName);
            sleep(1000);
        }
    }
}

์‹คํ—˜ ๋ชฉ์ : static synchronized ๋ฉ”์„œ๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค์—์„œ๋„ ํด๋ž˜์Šค ๋ ˆ๋ฒจ์˜ ๋ฝ์„ ๊ณต์œ ํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ƒ ๊ฒฐ๊ณผ

  • ์„œ๋กœ ๋‹ค๋ฅธ MyTask ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ–ˆ์ง€๋งŒ, static synchronized ํ‚ค์›Œ๋“œ๋กœ ์ธํ•ด ํด๋ž˜์Šค ๋ ˆ๋ฒจ์˜ ๋ฝ์„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ์†Œ์œ ํ•œ ๋™์•ˆ, ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋Š” BLOCKED ์ƒํƒœ๊ฐ€ ๋˜์–ด ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ: ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ๋กœ๊ทธ๋ฅผ ๋‚จ๊น๋‹ˆ๋‹ค. ํด๋ž˜์Šค ๋ ˆ๋ฒจ์˜ ๋ฝ์ด ๊ณต์œ ๋˜๊ณ  ์žˆ์Œ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

synchronized ํ‚ค์›Œ๋“œ๋Š” ์ธ์Šคํ„ด์Šค๋งˆ๋‹ค ๊ณ ์œ ํ•œ ๋ชจ๋‹ˆํ„ฐ ๋ฝ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ๋™์ผ ์ธ์Šคํ„ด์Šค์—์„œ ๋™๊ธฐํ™”๋œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๊ฒฝ์šฐ, ์Šค๋ ˆ๋“œ๋Š” ๋ฝ์„ ์–ป๊ธฐ ์œ„ํ•ด BLOCKED ์ƒํƒœ๋กœ ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค.

static synchronized ํ‚ค์›Œ๋“œ๋Š” ํด๋ž˜์Šค ๋ ˆ๋ฒจ์˜ ๋ชจ๋‹ˆํ„ฐ ๋ฝ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์„œ๋กœ ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค๋ผ๋„ ๋™์ผํ•œ ๋ฝ์„ ๊ณต์œ ํ•˜์—ฌ, ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ์ ์œ ํ•˜๋ฉด ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋Š” ๋Œ€๊ธฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

3. synchronized ๋‹จ์ ์„ ๊ทน๋ณตํ•œ ReentrantLock ์†Œ๊ฐœ

synchronized ํ‚ค์›Œ๋“œ๋Š” ๋™๊ธฐํ™”๋ฅผ ์ œ๊ณตํ•˜์ง€๋งŒ, ๋ฌดํ•œ ๋Œ€๊ธฐ์™€ ๋น„๊ณต์ •์„ฑ์ด๋ผ๋Š” ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Java 5๋ถ€ํ„ฐ ReentrantLock์ด ๋„์ž…๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ReentrantLock์€ LockSupport๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์œ„ ๋‹จ์ ์„ ๊ทน๋ณตํ•ฉ๋‹ˆ๋‹ค.

 

1. ๋ฌดํ•œ ๋Œ€๊ธฐ ๊ทน๋ณต

LockSupport๋Š” concurrent ํŒจํ‚ค์ง€์— ์œ„์น˜ํ•˜๋ฉฐ, ์ €์ˆ˜์ค€์˜ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. LockSupport๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ํš๋“ํ•˜๋ ค๊ณ  ๋Œ€๊ธฐํ•  ๋•Œ, ์Šค๋ ˆ๋“œ์˜ ์ƒํƒœ๋ฅผ BLOCKED ๋Œ€์‹  WAITING์œผ๋กœ ๋ณ€๊ฒฝํ•ด์„œ ์ธํ„ฐ๋ŸฝํŠธ๋ฅผ ํ—ˆ์šฉํ•˜๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด synchronized์˜ ๋ฌดํ•œ ๋Œ€๊ธฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

 

2. ๊ณต์ •์„ฑ ๊ทน๋ณต

ReentrantLock์€ ๊ณต์ • ๋ชจ๋“œ์™€ ๋น„๊ณต์ • ๋ชจ๋“œ๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, ํ•„์š”์— ๋”ฐ๋ผ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

2-1. ๊ณต์ • ๋ชจ๋“œ (Fair Mode)

ํŠน์ง•

  • ๋Œ€๊ธฐ ํ์— ์žˆ๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ์š”์ฒญํ•œ ์ˆœ์„œ๋Œ€๋กœ ๋ฝ์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค 
  • ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ์–ธ์  ๊ฐ€๋Š” ๋ฝ์„ ํš๋“ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.(๊ธฐ์•„ ํ˜„์ƒ ๋ฐฉ์ง€)

๋‹จ์ 

  • ๋Œ€๊ธฐ ํ ๊ด€๋ฆฌ๋กœ ์ธํ•ด ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ ๋ฐฉ๋ฒ•

Lock lock = new ReentrantLock(true); // ๊ณต์ • ๋ชจ๋“œ ์„ค์ •

 

2-2. ๋น„๊ณต์ • ๋ชจ๋“œ (Non-Fair Mode)

ํŠน์ง•

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

์‚ฌ์šฉ ๋ฐฉ๋ฒ•

 
Lock lock = new ReentrantLock(); // ๊ธฐ๋ณธ์€ ๋น„๊ณต์ • ๋ชจ๋“œ

 

ReentrantLock์˜ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

ReentrantLock์€ ์ž„๊ณ„์˜์—ญ์— ๋Œ€ํ•ด ๋ช…์‹œ์ ์œผ๋กœ lock()๊ณผ unlock()์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
unlock()์€ ๋ฐ˜๋“œ์‹œ finally ๋ธ”๋ก์—์„œ ํ˜ธ์ถœํ•˜์—ฌ, ๋ฝ์ด ํ•ญ์ƒ ํ•ด์ œ๋˜๋„๋ก ๋ณด์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Lock lock = new ReentrantLock();

try {
    lock.lock(); // ๋ฝ ํš๋“
    // ์ž„๊ณ„์˜์—ญ
} finally {
    lock.unlock(); // ๋ฝ ํ•ด์ œ
}

ReentrantLock์€ Lock ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๋Œ€ํ‘œ์ ์ธ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ์ด ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ฃผ์š” ๋ฉ”์„œ๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 

 

void lock()

  • ๋ฝ์„ ํš๋“ํ•œ๋‹ค. ๋งŒ์•ฝ, ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ด๋ฏธ ๋ฝ์„ ํš๋“ ํ–ˆ๋‹ค๋ฉด ๋ฝ์ด ํ’€๋ฆด ๋•Œ๊นŒ์ง€ ํ˜„์žฌ ์Šค๋ ˆ๋“œ๋Š” WAITING ์ƒํƒœ๊ฐ€ ๋œ๋‹ค. ํ•˜์ง€๋งŒ ์ธํ„ฐ๋ŸฝํŠธ์— ์‘๋‹ตํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • WAITING ์ƒํƒœ์ง€๋งŒ ์™œ ์ธํ„ฐ๋ŸฝํŠธ์— ์‘๋‹ตํ•˜์ง€ ์•Š๋Š” ์ด์œ ?
    • ๋‚ด๋ถ€ ๊ตฌํ˜„์—์„œ ์ธํ„ฐ๋ŸฝํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ˆœ๊ฐ„ WAITING ์ƒํƒœ๋ฅผ ๋น ์ ธ๋‚˜์™€์„œ RUNNABLE ์ƒํƒœ๊ฐ€ ๋˜์ง€๋งŒ, ๋‹ค์‹œ ํ•ด๋‹น ์Šค๋ ˆ๋“œ๋ฅผ WAITING ์ƒํƒœ๋กœ ๊ฐ•์ œ๋กœ ๋ณ€๊ฒฝํ•ด๋ฒ„๋ฆฐ๋‹ค.
  • ์ด ๋ฝ์€ ๋ชจ๋‹ˆํ„ฐ ๋ฝ์ด ์•„๋‹ˆ๊ณ , Lock ์ธํ„ฐํŽ˜์ด์Šค์™€ ReentrantLock ์ด ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋ฉฐ, ๋ชจ๋‹ˆํ„ฐ ๋ฝ๊ณผ BLOCKED ์ƒํƒœ๋Š” synchronized ์—์„œ๋งŒ ์‚ฌ์šฉ๋œ๋‹ค.

void lockInterruptibly()

  • ๋ฝ ํš๋“์„ ์‹œ๋„ํ•˜๋˜, ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ธํ„ฐ๋ŸฝํŠธ๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ WAITING ์ƒํƒœ์—์„œ ๋ฒ—์–ด๋‚˜์„œ ๋ฝ ํš๋“์„ ํฌ๊ธฐ ํ•  ์ˆ˜ ์žˆ๋‹ค.

boolean tryLock()

  • ๋ฝ ํš๋“์„ ์‹œ๋„ํ•˜๊ณ , ์ฆ‰์‹œ ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜. ๋ฝ์„ ํš๋“ํ•˜๋ฉด true, ์•„๋‹ˆ๋ฉด false

boolean tryLock(long time, TimeUnit unit)

  • ์ฃผ์–ด์ง„ ์‹œ๊ฐ„ ์•ˆ์— ๋ฝ์„ ํš๋“ํ•˜๋ฉด true ๋ฅผ ๋ฐ˜ํ™˜, ์ฃผ์–ด์ง„ ์‹œ๊ฐ„ ์•ˆ์— ๋ฝ ํš๋“ ๋ชปํ•˜๋ฉด false ๋ฐ˜ํ™˜. ๋ฝ ํš๋“ ๋Œ€๊ธฐ ์ค‘ ์ธํ„ฐ๋ŸฝํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๋ฝ ํš๋“์„ ํฌ๊ธฐํ•œ๋‹ค.

void unlock()

  • ๋ฝ์„ ํš๋“ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ํ˜ธ์ถœํ•ด์•ผ ํ•˜๋ฉฐ, ๋ฝ์„ ํ•ด์ œํ•œ๋‹ค.

Condition newCondition()

  • Condition ๊ฐ์ฒด๋Š” ๋ฝ๊ณผ ๊ฒฐํ•ฉ๋˜์–ด ์‚ฌ์šฉ๋˜๋ฉฐ, ์Šค๋ ˆ๋“œ๊ฐ€ ํŠน์ • ์กฐ๊ฑด์„ ๊ธฐ๋‹ค๋ฆฌ๊ฑฐ๋‚˜ ์‹ ํ˜ธ๋ฅผ ๋ฐ›๋Š”๋‹ค. Object ํด๋ž˜์Šค์˜ wait, notify ๋ฉ”์„œ๋“œ์™€ ์œ ์‚ฌํ•œ ์—ญํ• ์„ ํ•œ๋‹ค.

 

4. synchronized vs ReentrantLock

synchronized์™€ ReentrantLock์€ ๋ชจ๋‘ ๋™๊ธฐํ™” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋‘˜ ์‚ฌ์ด์—๋Š” ๋ช‡ ๊ฐ€์ง€ ์ค‘์š”ํ•œ ์ฐจ์ด์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

1. ์ œ๊ณต ๋ฐฉ์‹

  • synchronized๋Š” ์ž๋ฐ” ์–ธ์–ด ์ฐจ์›์—์„œ ์ œ๊ณตํ•˜๋Š” ํ‚ค์›Œ๋“œ๋กœ ๋ชจ๋“  ์ธ์Šคํ„ด์Šค์—๋Š” ๋ชจ๋‹ˆํ„ฐ ๋ฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค.(wait set ํฌํ•จ)
  • ReentrantLock์€ Java์˜ java.util.concurrent ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ์ ์œผ๋กœ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

2. ์Šค๋ ˆ๋“œ ๋Œ€๊ธฐ ์ƒํƒœ

  • synchronized๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ƒํƒœ๋Š” BLOCKED์ž…๋‹ˆ๋‹ค. ์ด ์ƒํƒœ์—์„œ๋Š” ์ธํ„ฐ๋ŸฝํŠธ๋ฅผ ํ†ตํ•ด ์Šค๋ ˆ๋“œ๋ฅผ ๊นจ์šธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
  • ๋ฐ˜๋ฉด, ReentrantLock์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ƒํƒœ๋Š” WAITING ๋˜๋Š” TIMED_WAITING์œผ๋กœ, ์ธํ„ฐ๋ŸฝํŠธ๋ฅผ ํ†ตํ•ด ๋Œ€๊ธฐ๋ฅผ ์ค‘๋‹จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ์ƒ์‚ฐ์ž-์†Œ๋น„์ž ๋ฌธ์ œ, ํ•œ์ •๋œ ๋ฒ„ํผ ๋ฌธ์ œ์—์„œ์˜ ํšจ์œจ์„ฑ

  • synchronized๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ๋Œ€๊ธฐ ์ง‘ํ•ฉ(wait set)์—์„œ ์›ํ•˜๋Š” ์Šค๋ ˆ๋“œ๋งŒ ์„ ํƒ์ ์œผ๋กœ ๊นจ์šธ ์ˆ˜ ์—†์–ด์„œ ๋น„ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.(๋น„ํšจ์œจ์ ์ด์ง€ ์ƒ์‚ฐ์ž-์†Œ๋น„์ž ๋ฌธ์ œ์™€ ํ•œ์ •๋œ ๋ฒ„ํผ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜๋Š” ์žˆ์Šต๋‹ˆ๋‹ค.)
  • ReentrantLock์€ Condition ๊ฐ์ฒด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ์Šค๋ ˆ๋“œ๋งŒ ์„ ํƒ์ ์œผ๋กœ ๊นจ์šธ ์ˆ˜ ์žˆ์–ด ์ƒ์‚ฐ์ž-์†Œ๋น„์ž ๋ฌธ์ œ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

* ์ƒ์‚ฐ์ž-์†Œ๋น„์ž ๋ฌธ์ œ์— ๋Œ€ํ•ด synchronized ์™€ ReentrantLock ์ฐจ์ด๋ฅผ ๋” ์ž์„ธํžˆ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด ๊น€์˜ํ•œ๋‹˜์˜ ๊ณ ๊ธ‰ ์ž๋ฐ” ๊ฐ•์˜๋ฅผ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

 

 

๋งˆ์น˜๋ฉฐ..

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” synchronized์™€ ReentrantLock์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๊ทธ๋™์•ˆ Async์™€ Sync์˜ ์ฐจ์ด, Non-Blocking๊ณผ Blocking์˜ ์ฐจ์ด๋ฅผ ์ดํ•ดํ–ˆ๊ธฐ์— ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ๋ฅผ ์ถฉ๋ถ„ํžˆ ์ดํ•ดํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋˜ ์ œ ์ž์‹ ์„ ๋Œ์•„๋ณด๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ํ•™์Šต์„ ํ†ตํ•ด Java๋Š” ์• ์ดˆ์— ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ๋ฅผ ์—ผ๋‘์— ๋‘๊ณ  ์„ค๊ณ„๋œ ์–ธ์–ด์ด๋ฉฐ, ์ด๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•œ ๋‹ค์–‘ํ•œ ๋™๊ธฐํ™” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค๋Š” ์ ์„ ์ƒˆ๋กญ๊ฒŒ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.

 

์ด ๊ธ€์„ ์ž‘์„ฑํ•˜๋ฉฐ synchronized ๋Š” ์Šค๋ ˆ๋“œ ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์ด ํ•„์š” ์—†๋Š” ๊ฐ„๋‹จํ•œ ๋™๊ธฐํ™”์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๊ณ , ReentrantLock ์€ concurrent ํŒจํ‚ค์ง€์— ์žˆ๋Š” BlockingQueue์˜ ๋‚ด๋ถ€ ๊ตฌํ˜„์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ, BlockingQueue ๋Š” ์Šค๋ ˆ๋“œ ๊ฐ„์˜ ๋ฐ์ดํ„ฐ ์ „๋‹ฌํ•˜๋Š” ์ƒํ™ฉ(์ƒ์‚ฐ์ž-์†Œ๋น„์ž ๋ฌธ์ œ ๊ฐ™์ด)์— ์‚ฌ์šฉํ•˜๊ธฐ์— ์ ํ•ฉํ•˜๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ญ๋‹ˆ๋‹ค.

์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

 

 

์ถœ์ฒ˜

- ๊น€์˜ํ•œ๋‹˜์˜ ๊ณ ๊ธ‰ ์ž๋ฐ” ๊ฐ•์˜

- Stackoverflow: What`s a monitor in Java?

- ํ…Œ์ฝ”๋ธ”: Java๋กœ ๋™๊ธฐํ™”๋ฅผ ํ•ด๋ณด์ž!