๐Ÿ“š [Java] - ReentrantLock, ReadWriteLock, ReentrantReadWriteLock

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

์ž๋ฐ”

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

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


๊ธฐ์กด ๋ฌธ์ œ์ 

์™œ ์šฐ๋ฆฌ๋Š” ๋ฝ(Lock)์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฑธ๊นŒ? ์†”์งํ•˜๊ฒŒ ๋‚˜๋Š” ๋ฝ(Lock)์— ๋Œ€ํ•ด ์•ˆ ์ง€ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•˜๋‹ค.

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

์ผ๋‹จ ๋ฝ(Lock)์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€ ์ฝ”๋“œ๋กœ ํ™•์ธํ•ด๋ณด์ž

public class NoLockExample {

    static int count = 0;

    public static void main(String[] args) {
        Thread a = new Thread(addCount("A "));
        Thread b = new Thread(addCount("B "));
        Thread c = new Thread(addCount("C "));
        Thread d = new Thread(addCount("D "));
        Thread e = new Thread(addCount("E "));

        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }

    static Runnable addCount(String threadName) {
        return () -> IntStream.range(0, 20000).forEach(i -> {
            System.out.println(threadName + count++);
        });
    }
}

์šฐ๋ฆฌ๋Š” 5๊ฐœ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ๊ฐ 20,000๋ฒˆ์”ฉ count๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋‹ˆ๊นŒ, ์ตœ์ข…์ ์œผ๋กœ 99999๊นŒ์ง€ ์ฆ๊ฐ€ํ•  ๊ฒƒ์„ ์˜ˆ์ƒํ•œ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด, ๊ฒฐ๊ณผ๋Š” 99,989๊นŒ์ง€๋งŒ ์ฆ๊ฐ€ํ•˜๋Š” ๋“ฑ ์˜ˆ์ƒ๊ณผ ๋‹ฌ๋ฆฌ ๊ฐ’์ด ๋ˆ„๋ฝ๋˜๋Š” ํ˜„์ƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ ์ด์œ ๋Š” count++ ์—ฐ์‚ฐ์ด ์ฝ๊ธฐ โ†’ ๋”ํ•˜๊ธฐ โ†’ ์ €์žฅ์˜ 3๋‹จ๊ณ„๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๊ณ , ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•˜๋ฉด ์„œ๋กœ ๋ฎ์–ด์“ฐ๊ธฐ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ฆ‰, ๋ฝ์„ ๊ฑธ์ง€ ์•Š์œผ๋ฉด ์Šค๋ ˆ๋“œ ๊ฐ„ ๊ฐ„์„ญ์œผ๋กœ ์ธํ•ด ๊ฐ’์ด ๊ผฌ์ด๋Š” ๋ฌธ์ œ(race condition)๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.

๋ฌผ๋ก  ๊ฐ„๋‹จํ•˜๊ฒŒ ์˜ˆ์ „์— ์ •๋ฆฌํ–ˆ๋˜ synchronized ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฐธ๊ณ 
๐Ÿ“š [Java] - synchronized

public class LockExample {

    static int count = 0;

    public static void main(String[] args) {
        Thread a = new Thread(addCount("A "));
        Thread b = new Thread(addCount("B "));
        Thread c = new Thread(addCount("C "));
        Thread d = new Thread(addCount("D "));
        Thread e = new Thread(addCount("E "));

        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }

    static Runnable addCount(String threadName) {
        return () -> IntStream.range(0, 20000).forEach(i -> {
            incrementCount(threadName);
        });
    }

    static synchronized void incrementCount(String threadName) {
        System.out.println(threadName + count++);
    }
}

ํ•˜์ง€๋งŒ synchronized๋Š” thread-Safeํ•œ ์ž‘์—…์ด ๊ฐ€๋Šฅํ–ˆ์ง€๋งŒ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์ƒ๊ธฐ๊ธฐ ์‰ฝ๊ณ  , ์„ธ๋ฐ€ํ•œ ์ œ์–ด๊ฐ€ ์–ด๋ ต๊ณ , ์ฝ๊ธฐ/์“ฐ๊ธฐ ๊ตฌ๋ถ„์ด ์•ˆ ๋˜์–ด ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ํšจ์œจ์ด ๋–จ์–ด์ง„๋‹ค.

์ด๋Ÿฐ ๋‹จ์ ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‚˜์˜จ ๊ฒƒ์ด ReentrantLock๊ณผ ReentrantReadWriteLock ์ด๋‹ค.


๐Ÿ“Œ ReentrantLock

  • ์ž๋ฐ”์—์„œ ๋™์‹œ์— ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์„ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•œ ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜ ์ค‘ ํ•˜๋‚˜๋‹ค.
void lock() // lock ์ž ๊ธˆ
void unlock() // lock ํ•ด์ œ
boolean isLocked() // lock์ด ์ž ๊ฒผ๋Š”์ง€ ํ™•์ธ
boolean tryLock() // lock polling
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException
  • ์ˆ˜๋™์œผ๋กœ ๋ฝ์„ ๊ฑธ๊ณ (lock()), ํ’€ ์ˆ˜(unlock()) ์žˆ๋Š” ๊ฐ์ฒด

  • ์ฆ‰, ์ž ๊ธˆ ์˜์—ญ์˜ ์‹œ์ž‘๊ณผ ๋์„ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค.

  • ReentrantLock์€ ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ ์žฌ์ง„์ž… ๊ฐ€๋Šฅ(Reentrant) โ†’ ๊ฐ™์€ ์Šค๋ ˆ๋“œ๊ฐ€ ์ด๋ฏธ ํš๋“ํ•œ ๋ฝ์„ ๋‹ค์‹œ ํš๋“ํ•  ์ˆ˜ ์žˆ์Œ

  • tryLock(), ๊ณต์ •์„ฑ ์˜ต์…˜(fairness) ๋“ฑ synchronized๋ณด๋‹ค ์œ ์—ฐํ•œ ๊ธฐ๋Šฅ ์ œ๊ณตํ•œ๋‹ค.

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

public class LockExample {

    static int count = 0;
    static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        Thread a = new Thread(addCount("A "));
        Thread b = new Thread(addCount("B "));
        Thread c = new Thread(addCount("C "));
        Thread d = new Thread(addCount("D "));
        Thread e = new Thread(addCount("E "));

        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }

    static Runnable addCount(String threadName) {
        return () -> IntStream.range(0, 20000).forEach(i -> {
            incrementCount(threadName);
        });
    }

    static void incrementCount(String threadName) {
        lock.lock(); // ๋ฝ ํš๋“
        try {
            System.out.println(threadName + count++);
        } finally {
            lock.unlock(); // ๋ฐ˜๋“œ์‹œ ๋ฝ ํ•ด์ œ
        }
    }
}

์—ญ์‹œ ์˜ˆ์ƒ๋Œ€๋กœ 99999 ๊ฐ€ ๋‚˜์˜ค๋Š” ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๋ฝ(Lock)์„ ๊ฐ€์ง„ ์ฑ„ ์˜ค๋žซ๋™์•ˆ ์ž‘์—…์„ ํ•˜๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ์žˆ์œผ๋ฉด, ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋“ค์€ ๊ทธ ๋ฝ์„ ๋ชป ์–ป์–ด์„œ ๊ธฐ๋‹ค๋ ค์•ผ ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ ๊ณต์œ  ๋ฐ์ดํ„ฐ๋ฅผ ๋ณดํ˜ธํ•˜๋Š” ๊ฒƒ๋งŒํผ, ๋ฝ์„ ์˜ค๋ž˜ ์žก๊ณ  ์žˆ์ง€ ์•Š๊ฒŒ ํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜๋‹ค.

์ž๋ฐ”์—์„œ๋Š” ์ด๋Ÿฐ ์ƒํ™ฉ์„ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•ด wait(), notify(), notifyAll() ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

wait()

์‹คํ–‰ ์ค‘์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ๋ฐ˜๋‚ฉํ•˜๊ณ  ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ํ•œ๋‹ค.

waiting pool์ด๋ผ๋Š” ๊ณณ์—์„œ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๋‹ค๋ฆผ

๋ฝ์„ ์žก์€ ์ƒํƒœ์—์„œ ์˜ค๋žซ๋™์•ˆ ๋ฉˆ์ถฐ์žˆ๋Š” ๊ฑธ ๋ฐฉ์ง€ํ•œ๋‹ค.

notify()

waiting pool์— ์žˆ๋Š” ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ์—๊ฒŒ ์ž‘์—…์„ ๊ณ„์†ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์•Œ๋ฆผ

์–ด๋–ค ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ์–ป์„์ง€๋Š” ๋ณด์žฅ ๋ชปํ•จ โ†’ JVM ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ๊ฒฐ์ •

notifyAll()

waiting pool์— ์žˆ๋Š” ๋ชจ๋“  ์Šค๋ ˆ๋“œ์—๊ฒŒ ์•Œ๋ฆผ

๋‹จ, ๋ฝ์€ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ํš๋“ ๊ฐ€๋Šฅ โ†’ ๋‚˜๋จธ์ง€๋Š” ๋ฝ์„ ์–ป๊ธฐ ์œ„ํ•ด ๋‹ค์‹œ ๊ธฐ๋‹ค๋ฆผ

synchronized / ObjectReentrantLock / Condition์„ค๋ช…
synchronizedlock() / unlock()๋ฝ์„ ํš๋“ํ•˜๊ณ  ํ•ด์ œ. synchronized๋Š” ๋ธ”๋ก ๋‹จ์œ„, ReentrantLock์€ ์ˆ˜๋™ ์ œ์–ด
wait()await()๋ฝ์„ ๋ฐ˜๋‚ฉํ•˜๊ณ  ๋Œ€๊ธฐ. Condition ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ๊ธฐ๋‹ค๋ฆผ
notify()signal()waiting pool์— ์žˆ๋Š” ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋ฅผ ๊นจ์›Œ์„œ ๋ฝ์„ ๋‹ค์‹œ ์–ป์–ด ์ž‘์—… ์ง„ํ–‰
notifyAll()signalAll()waiting pool์— ์žˆ๋Š” ๋ชจ๋“  ์Šค๋ ˆ๋“œ์—๊ฒŒ ์•Œ๋ฆผ, ๋ฝ์€ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์”ฉ๋งŒ ์–ป์„ ์ˆ˜ ์žˆ์Œ

๐Ÿค” Condition์€ Lock๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋ฉฐ,
ํŠน์ • ์กฐ๊ฑด์ด ๋งŒ์กฑ๋  ๋•Œ๊นŒ์ง€ ์Šค๋ ˆ๋“œ๋ฅผ ๋Œ€๊ธฐ(wait) ์‹œํ‚ค๊ฑฐ๋‚˜, ์กฐ๊ฑด์ด ๋งŒ์กฑ๋˜๋ฉด ๋Œ€๊ธฐ ์ค‘์ธ ์Šค๋ ˆ๋“œ๋ฅผ ๊นจ์šฐ๋Š”(signal) ์—ญํ• ์„ ํ•œ๋‹ค.

  • ์ฆ‰, "์กฐ๊ฑด์ด ์ถฉ์กฑ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค!" โ†’ "์กฐ๊ฑด์ด ๋์–ด, ์ด์ œ ์ผ์–ด๋‚˜!" ์ด๋Ÿฐ ์‹์˜ ํ๋ฆ„์„ ๋ฝ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฐ์ฒด๋‹ค.

ReadWriteLock

public interface ReadWriteLock {

    // ์ฝ๊ธฐ ์ „์šฉ ๋ฝ์„ ๋ฐ˜ํ™˜
    Lock readLock();

    // ์“ฐ๊ธฐ ์ „์šฉ ๋ฝ์„ ๋ฐ˜ํ™˜
    Lock writeLock();
}

ReadWriteLock์€ ์ฝ๊ธฐ ๋ฝ(read lock) ๊ณผ ์“ฐ๊ธฐ ๋ฝ(write lock) ๋‘ ์ข…๋ฅ˜์˜ ๋ฝ์„ ์ œ๊ณตํ•˜๋Š” ๋ฝ ์ธํ„ฐํŽ˜์ด์Šค๋‹ค.

  • ์ฝ๊ธฐ ๋ฝ (Read Lock)

    • ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ธฐ๋งŒ ํ•˜๋Š” ์ž‘์—…์—์„œ๋Š” ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•ด๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋™์‹œ ์ ‘๊ทผ์ด ์•ˆ์ „ํ•˜๋‹ค.
      โ†’ ๋”ฐ๋ผ์„œ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ฝ๊ธฐ ๋ฝ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.
  • ์“ฐ๊ธฐ ๋ฝ (Write Lock)

    • ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ž‘์—…์€ ๋‹จ ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ์ ‘๊ทผํ•ด์•ผ ํ•œ๋‹ค.
      โ†’ ์“ฐ๊ธฐ ๋ฝ์€ ๋ฐฐํƒ€์ (exclusive) ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.
      โ†’ ์“ฐ๊ธฐ ๋ฝ์ด ๊ฑธ๋ฆฐ ๋™์•ˆ์€ ๋‹ค๋ฅธ ์ฝ๊ธฐ/์“ฐ๊ธฐ ๋ฝ ๋ชจ๋‘ ํš๋“ํ•  ์ˆ˜ ์—†๋‹ค.

๊ฐ€์žฅ ๋งŽ์ด ์“ฐ์ด๋Š” ๊ตฌํ˜„์ฒด๋Š” ReentrantReadWriteLock์ด ์กด์žฌํ•œ๋‹ค.

ReadWriteLock lock = new ReentrantReadWriteLock();

Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();

์™œ ์‚ฌ์šฉํ• ๊นŒ?

๐Ÿ’ก ์ผ๋ฐ˜ Lock (ReentrantLock)์˜ ํ•œ๊ณ„

๋ณดํ†ต ReentrantLock์ด๋‚˜ synchronized๋Š”
ํ•œ ๋ฒˆ์— ๋‹จ ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

์ฆ‰, ๋ฐ์ดํ„ฐ ์ฝ๊ธฐ๋งŒ ํ•ด๋„ ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ์ˆœ์„œ๋Œ€๋กœ ๊ธฐ๋‹ค๋ ค์•ผ ํ•œ๋‹ค.

Thread A: read() ์‹คํ–‰ ์ค‘
Thread B: ๊ธฐ๋‹ค๋ฆผ 
Thread C: ๊ธฐ๋‹ค๋ฆผ 

ํ•˜์ง€๋งŒ ์ฝ๊ธฐ(read) ์ž‘์—…์€ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์—,
์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ฝ์–ด๋„ ์•ˆ์ „ํ•˜๋‹ค.

์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ ๋‹จ์ผ ๋ฝ์„ ์“ฐ๋ฉด ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์„ฑ๋Šฅ์ด ๋–จ์–ด์ง„๋‹ค.

๊ทธ๋ž˜์„œ ๋“ฑ์žฅํ•œ ๊ฒŒ ReadWriteLock์ด๋‹ค.

์ด๊ฑด ์ฝ๊ธฐ ์ „์šฉ ๊ตฌ๊ฐ„์€ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•˜๊ฒŒ ํ•˜๊ณ ,
์“ฐ๊ธฐ ๊ตฌ๊ฐ„์€ ์˜ค์ง ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ์ ‘๊ทผํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค.

์–ธ์ œ ์‚ฌ์šฉํ•ด์•ผํ• ๊นŒ?

์ƒํ™ฉ์„ค๋ช…
์ฝ๊ธฐ ๋น„์ค‘์ด ๋†’์„ ๋•Œ๋Œ€๋ถ€๋ถ„์˜ ์ž‘์—…์ด ์ฝ๊ธฐ๋ผ๋ฉด, ๋™์‹œ์— ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ด์„œ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋จ
์“ฐ๊ธฐ ๋น„์ค‘์ด ๋‚ฎ์„ ๋•Œ๊ฐ€๋”๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค๋ฉด, ์ž ๊ธˆ ๊ฒฝํ•ฉ(lock contention)์„ ์ค„์ž„
์บ์‹œ, ์„ค์ •๊ฐ’, ์กฐํšŒ API ๋“ฑ์ฝ๊ธฐ๊ฐ€ ๋งŽ๊ณ  ์“ฐ๊ธฐ๊ฐ€ ๋“œ๋ฌธ ๊ณณ์—์„œ ๋งค์šฐ ์ ํ•ฉ

ReentrantReadWriteLock

ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();

ReentrantReadWriteLock์€ ์ฝ๊ธฐ ์ž‘์—…๊ณผ ์“ฐ๊ธฐ ์ž‘์—…์„ ๊ตฌ๋ถ„ํ•˜์—ฌ ๋™์‹œ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•œ ๋ฝ์ด๋‹ค.

์—ฌ๋Ÿฌ ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ฝ๊ธฐ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด๋„ ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ์ด ๊นจ์ง€์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ฝ๊ธฐ ๋ฝ(ReadLock) ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

์ฆ‰, ์ฝ๊ธฐ ๋ฝ์€ ๊ณต์œ  ๋ฝ(Shared Lock)์œผ๋กœ ๋™์ž‘ํ•˜์—ฌ ์ƒํ˜ธ ๋ฐฐ์ œ ์—†์ด ๋ณ‘๋ ฌ์ ์ธ ์ฝ๊ธฐ ์ ‘๊ทผ์„ ํ—ˆ์šฉํ•˜๋ฏ€๋กœ, ์ฝ๊ธฐ ์ž‘์—…์ด ๋งŽ์€ ํ™˜๊ฒฝ์—์„œ ๋™์‹œ์„ฑ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋œ๋‹ค.

๋ฐ˜๋ฉด ์“ฐ๊ธฐ ๋ฝ(WriteLock) ์€ ๋ฐฐํƒ€์ (Exclusive)์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ์“ฐ๊ธฐ ๋ฝ์„ ๋ณด์œ ํ•˜๊ณ  ์žˆ๋Š” ๋™์•ˆ์—๋Š” ๋‹ค๋ฅธ ์–ด๋–ค ์Šค๋ ˆ๋“œ๋„ ์ฝ๊ธฐ ๋ฝ์ด๋‚˜ ์“ฐ๊ธฐ ๋ฝ์„ ํš๋“ํ•  ์ˆ˜ ์—†๋‹ค.

์ฝ๊ธฐ ๋ฝ์ด ์œ ์ง€๋˜๊ณ  ์žˆ๋Š” ๋™์•ˆ์—๋„ ์“ฐ๊ธฐ ๋ฝ์€ ํš๋“ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, ๋งŒ์•ฝ ์“ฐ๊ธฐ ๋ฝ์„ ์–ป์œผ๋ ค๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ๋Œ€๊ธฐ ์ค‘์ธ๋ฐ ๊ณ„์†ํ•ด์„œ ์ƒˆ๋กœ์šด ์ฝ๊ธฐ ์š”์ฒญ์ด ๋“ค์–ด์˜จ๋‹ค๋ฉด, ์“ฐ๊ธฐ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ์–ป์ง€ ๋ชปํ•˜๊ณ  ๊ธฐ์•„(starvation) ์ƒํƒœ์— ๋น ์งˆ ์ˆ˜ ์žˆ๋‹ค
.
์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ReentrantReadWriteLock์€ ์ƒ์„ฑ ์‹œ ๊ณต์ • ๋ชจ๋“œ(fair mode) ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ๊ฒฝ์šฐ ๋ฝ์€ ๋Œ€๊ธฐ์—ด์˜ ์ˆœ์„œ(FIFO)์— ๋”ฐ๋ผ ๋ถ€์—ฌ๋˜์–ด ๊ธฐ์•„ ํ˜„์ƒ์ด ์™„ํ™”๋œ๋‹ค.

์ฝ๊ธฐ ๋ฝ์ด ํ•ด์ œ๋  ๋•Œ(unlock() ํ˜ธ์ถœ ์‹œ), ๋‚ด๋ถ€์ ์œผ๋กœ ๋ณด์œ  ์ค‘์ธ ์ฝ๊ธฐ ๋ฝ์˜ ์ˆ˜๊ฐ€ ํ•˜๋‚˜์”ฉ ์ค„์–ด๋“ค๋ฉฐ, ๋ชจ๋“  ์ฝ๊ธฐ ๋ฝ์ด ํ•ด์ œ๋˜์–ด ๊ฐœ์ˆ˜๊ฐ€ 0์ด ๋˜๋ฉด ์“ฐ๊ธฐ ๋ฝ์„ ๋Œ€๊ธฐ ์ค‘์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ํš๋“ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ, ReentrantReadWriteLock์—์„œ ์“ฐ๊ธฐ ๋ฝ์€ Condition ๊ฐ์ฒด๋ฅผ ์ง€์›ํ•˜์ง€๋งŒ, ์ฝ๊ธฐ ๋ฝ์€ Condition์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ readLock.newCondition()์„ ํ˜ธ์ถœํ•˜๋ฉด UnsupportedOperationException์ด ๋ฐœ์ƒํ•œ๋‹ค.


๐Ÿงน์ •๋ฆฌ

๊ตฌ๋ถ„ReadWriteLockReentrantReadWriteLockReentrantLock
ํƒ€์ž…์ธํ„ฐํŽ˜์ด์Šค (interface)๊ตฌํ˜„ ํด๋ž˜์Šค (class)๊ตฌํ˜„ ํด๋ž˜์Šค (class)
๋ฝ์˜ ์ข…๋ฅ˜์ฝ๊ธฐ ๋ฝ, ์“ฐ๊ธฐ ๋ฝ ๋‘ ๊ฐ€์ง€ ์ œ๊ณต์ฝ๊ธฐ ๋ฝ(ReadLock), ์“ฐ๊ธฐ ๋ฝ(WriteLock) ๋ชจ๋‘ ๊ตฌํ˜„๋‹จ์ผ ๋ฝ (์ฝ๊ธฐ/์“ฐ๊ธฐ ๊ตฌ๋ถ„ ์—†์Œ)
๋™์ž‘ ๋ฐฉ์‹์ฝ๊ธฐ/์“ฐ๊ธฐ ๊ฐœ๋…๋งŒ ์ •์˜์ฝ๊ธฐ ๋ฝ์€ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณต์œ  ๊ฐ€๋Šฅ, ์“ฐ๊ธฐ ๋ฝ์€ ๋‹จ๋… ์‚ฌ์šฉํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ (๋ฐฐํƒ€์ )
์žฌ์ง„์ž… ๊ฐ€๋Šฅ ์—ฌ๋ถ€์ •์˜ ์—†์Œ์žฌ์ง„์ž… ๊ฐ€๋Šฅ์žฌ์ง„์ž… ๊ฐ€๋Šฅ
๊ณต์ •์„ฑ ์„ค์ • (Fairness)์ •์˜ ์—†์Œ์ง€์› (new ReentrantReadWriteLock(true))์ง€์› (new ReentrantLock(true))
Condition ์ง€์› ์—ฌ๋ถ€์ •์˜ ์—†์ŒWriteLock๋งŒ ์ง€์› / ReadLock์€ ์ง€์›ํ•˜์ง€ ์•Š์Œ์ง€์›
๋™์‹œ์„ฑ ์ˆ˜์ค€๊ฐœ๋…์ƒ ์ค‘๊ฐ„๋†’์Œ (์ฝ๊ธฐ ๋ณ‘๋ ฌ ํ—ˆ์šฉ)๋‚ฎ์Œ (๋‹จ์ผ ์ ‘๊ทผ)
์ฃผ์š” ๋ฉ”์„œ๋“œreadLock(), writeLock()lock(), tryLock(), unlock(), getReadLockCount() ๋“ฑlock(), tryLock(), unlock(), newCondition()
์ ํ•ฉํ•œ ์ƒํ™ฉ์„ค๊ณ„์šฉ (์ง์ ‘ ์‚ฌ์šฉ ๋ถˆ๊ฐ€)์ฝ๊ธฐ ์ž‘์—…์ด ๋งŽ๊ณ  ์“ฐ๊ธฐ ์ž‘์—…์ด ์ ์€ ๊ฒฝ์šฐ๋‹จ์ˆœํ•œ ์ƒํ˜ธ ๋ฐฐ์ œ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ
๊ตฌํ˜„ ๊ธฐ๋ฐ˜์—†์Œ (์ธํ„ฐํŽ˜์ด์Šค)AbstractQueuedSynchronizer (AQS) ๊ธฐ๋ฐ˜AbstractQueuedSynchronizer (AQS) ๊ธฐ๋ฐ˜
๊ฐ์ฒด ์ƒ์„ฑ๋ถˆ๊ฐ€๋Šฅ (์ธํ„ฐํŽ˜์ด์Šค)new ReentrantReadWriteLock()new ReentrantLock()

์ฐธ๊ณ 

[Java] Lock, ReentrantLock, ReadWriteLock,

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

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