๐Ÿ” Java์—์„œ ReentrantLock ์™„์ „ ์ •๋ณตํ•˜๊ธฐ

JUHYUNยท2025๋…„ 6์›” 7์ผ
0
post-thumbnail

1. ๋“ค์–ด๊ฐ€๋ฉฐ

Java์˜ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ํ™˜๊ฒฝ์—์„œ ๋™๊ธฐํ™”๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค. ๊ฐ€์žฅ ํ”ํžˆ ์‚ฌ์šฉํ•˜๋Š” synchronized ์™ธ์—๋„, java.util.concurrent.locks ํŒจํ‚ค์ง€์— ํฌํ•จ๋œ ReentrantLock์€ ๋ณด๋‹ค ์ •๊ตํ•œ ์ œ์–ด๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ธ€์—์„œ๋Š” ReentrantLock์„ synchronized์™€ ๋น„๊ตํ•˜๋ฉฐ, ๊ทธ ๋‚ด๋ถ€ ์›๋ฆฌ์™€ ์–ด๋– ํ•œ ํŠน์ง•์ด ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

2. synchronized vs ReentrantLock

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๊ณต์œ  ์ž์›์„ ๋ณดํ˜ธํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ๋ฐฉ๋ฒ•์€ synchronized์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ณด๋‹ค ์œ ์—ฐํ•˜๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๋ฝ์ด ํ•„์š”ํ•  ๋•Œ๋Š” ReentrantLock์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฐจ์ด์ ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค

ํ•ญ๋ชฉsynchronizedReentrantLock
์„ ์–ธ ๋ฐฉ์‹ํ‚ค์›Œ๋“œ ์‚ฌ์šฉ (synchronized)๊ฐ์ฒด ์ƒ์„ฑ ํ›„ ๋ช…์‹œ์  lock/unlock
๊ณต์ •์„ฑ(Fairness) ์„ค์ •๋ถˆ๊ฐ€๋Šฅ๊ฐ€๋Šฅ (new ReentrantLock(true))
์ธํ„ฐ๋ŸฝํŠธ ๋Œ€์‘๋ถˆ๊ฐ€๋Šฅ๊ฐ€๋Šฅ (lockInterruptibly())
ํƒ€์ž„์•„์›ƒ ์„ค์ •๋ถˆ๊ฐ€๋Šฅ๊ฐ€๋Šฅ (tryLock(timeout))
Condition ์ง€์›wait/notifyCondition ๊ฐ์ฒด ์‚ฌ์šฉ
์ฝ”๋“œ ์œ ์—ฐ์„ฑ์ œํ•œ์ ๋†’์Œ (lock์„ ์—ฌ๋Ÿฌ ์œ„์น˜์—์„œ ์ œ์–ด ๊ฐ€๋Šฅ)
  • synchronized๋Š” ๊ฐ„๊ฒฐํ•˜๊ณ  ์ง๊ด€์ ์ด์ง€๋งŒ, ์„ธ๋ฐ€ํ•œ ๋™๊ธฐํ™” ์ œ์–ด์—๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ReentrantLock์€ ๋ช…์‹œ์ ์ธ lock/unlock ์ œ์–ด์™€ ๋‹ค์–‘ํ•œ ํ™•์žฅ ๊ธฐ๋Šฅ(๊ณต์ •์„ฑ, ์ธํ„ฐ๋ŸฝํŠธ, ํƒ€์ž„์•„์›ƒ, Condition ๋“ฑ)์„ ์ œ๊ณตํ•˜์—ฌ ๋ณต์žกํ•œ ๋™๊ธฐํ™” ์š”๊ตฌ์— ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ReentrantLock์˜ ๋‚ด๋ถ€ ๋™์ž‘ ์›๋ฆฌ

ReentrantLock์€ ๋‚ด๋ถ€์ ์œผ๋กœ AbstractQueuedSynchronizer(AQS)๋ผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ™œ์šฉํ•ด ๋ฝ ํš๋“๊ณผ ํ•ด์ œ๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ๋Š” AQS์˜ ๊ฐœ๋…๊ณผ, ReentrantLock์ด ์ด๋ฅผ ์–ด๋–ป๊ฒŒ ํ™•์žฅํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

3.1 AQS(AbstractQueuedSynchronizer)

AbstractQueuedSynchronizer๋Š” ๋ฝ, ์„ธ๋งˆํฌ์–ด ๋“ฑ ๋‹ค์–‘ํ•œ ๋™๊ธฐํ™” ๊ตฌ์กฐ์˜ ๊ธฐ๋ฐ˜์ด ๋˜๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.
์‹ค์ œ๋กœ ReentrantLock ์ด์™ธ์—๋„ Semaphore, CountDownLatch, ReentrantReadWriteLock ๋“ฑ์—์„œ AQS๋ฅผ ํ™œ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
ํ•ต์‹ฌ์€ int state ์ƒํƒœ๊ฐ’๊ณผ FIFO ๋ฐฉ์‹์˜ ๋Œ€๊ธฐ ํ๋ฅผ ํ†ตํ•ด ์Šค๋ ˆ๋“œ ๊ฐ„ ์ ‘๊ทผ์„ ์ œ์–ดํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

  • acquire(), release() ๋“ฑ ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ๋กœ ๋™๊ธฐํ™” ํ๋ฆ„์„ ์ •์˜
  • tryAcquire(), tryRelease() ๋“ฑ ๋ฉ”์„œ๋“œ๋ฅผ ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ ๊ตฌํ˜„ํ•˜๋„๋ก ์œ„์ž„
  • ๋Œ€๊ธฐ ์ค‘์ธ ์Šค๋ ˆ๋“œ๋Š” ๋‚ด๋ถ€ ํ์— Node ํ˜•ํƒœ๋กœ ๊ด€๋ฆฌ๋˜๋ฉฐ, LockSupport.park()/unpark()๋กœ ๋ธ”๋กœํ‚น ์ œ์–ด

3.1.1 AQS์˜ Node

์Šค๋ ˆ๋“œ๋“ค์ด FIFO ์ˆœ์„œ๋กœ ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ์— ๋ถ™๊ณ , ์•ž ๋…ธ๋“œ์˜ ์‹ ํ˜ธ์— ์˜ํ•ด ๋ฝ์„ ์ˆœ์ฐจ์ ์œผ๋กœ ํš๋“ํ•จ์œผ๋กœ์จ,
๊ณต์ •์„ฑ(fairness)๊ณผ ์บ์‹œ ํšจ์œจ์„ฑ(locality)์„ ๋™์‹œ์— ๋‹ฌ์„ฑํ•˜๋Š” ๋ฝ ๋Œ€๊ธฐ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด๋‹ค.

// AbstractQueuedSynchronizer.java
abstract static class Node {
	volatile Node prev;
	volatile Node next;
	Thread waiter;
    volatile int status;
...
}

โšก AQS์˜ ์บ์‹œ ํšจ์œจ์„ฑ

๋ฝ ์ƒํƒœ ํ™•์ธ ์‹œ ์•ž ๋…ธ๋“œ์˜ ์ƒํƒœ(status)๋งŒ ํ™•์ธ

  • ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์ผํ•œ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ(CAS ๋Œ€์ƒ ํ•„๋“œ)๋ฅผ ๋ฐ˜๋ณต์ ์œผ๋กœ ์ฝ๊ณ  ์“ด๋‹ค๋ฉด?

    • CPU ๊ฐ„ ์บ์‹œ ์ผ๊ด€์„ฑ(MESI protocol) ์œ ์ง€์— ๋”ฐ๋ฅธ ๋น„์šฉ ์ฆ๊ฐ€
    • ์—ฌ๋Ÿฌ ์ฝ”์–ด๊ฐ€ ๊ฐ™์€ ๋ณ€์ˆ˜์— ์“ฐ๊ธฐ๋ฅผ ์‹œ๋„ํ•˜๋ฉด์„œ ์บ์‹œ invalidation์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋ฐœ์ƒ
  • ์บ์‹œ ํšจ์œจ์„ฑ์„ ๋‹ฌ์„ฑํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜

๋ฉ”์ปค๋‹ˆ์ฆ˜์„ค๋ช…
์ฝ๊ธฐ ์ „์šฉ ๊ฐ์‹œ ๊ตฌ์กฐ์Šค๋ ˆ๋“œ๋Š” ๋Œ€๋ถ€๋ถ„ prev.status๋งŒ ์ฝ๊ธฐ ์ „์šฉ ์ ‘๊ทผ โ†’ ์บ์‹œ ํžˆํŠธ์œจ โ†‘
์“ฐ๊ธฐ ์ตœ์†Œํ™”๋Œ€๊ธฐ์—ด ์ง„์ž… ์‹œ tail์—๋งŒ CAS โ†’ ๊ณต์œ  ํ•„๋“œ ์“ฐ๊ธฐ ๊ฐ์†Œ, ์บ์‹œ ๊ฒฝํ•ฉ ์ตœ์†Œํ™”
๋…ธ๋“œ ๊ฐ„ ๋…๋ฆฝ์  ๋ฉ”๋ชจ๋ฆฌ ๋ฐฐ์น˜๊ฐ ์Šค๋ ˆ๋“œ๋Š” ์ž์‹ ๋งŒ์˜ ๋…ธ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์™€ ๋™์ผํ•œ ์บ์‹œ๋ผ์ธ์„ ๊ณต์œ ํ•˜์ง€ ์•Š์Œ โ†’ false sharing ๋ฐฉ์ง€
LockSupport ๊ธฐ๋ฐ˜ ๋ธ”๋กœํ‚น์Šคํ•€์„ ์ตœ์†Œํ™”ํ•˜์—ฌ ์บ์‹œ๋ผ์ธ invalidation ๋น„์šฉ ๊ฐ์†Œ

3.1.2 state์˜ ์˜๋ฏธ์™€ volatile ์ฒ˜๋ฆฌ

private volatile int state;
  • AQS์˜ ํ•ต์‹ฌ ํ•„๋“œ๋Š” volatile int state์ด๋ฉฐ, ๋ฝ์˜ ๋ณด์œ  ์—ฌ๋ถ€๋‚˜ ์žฌ์ง„์ž… ํšŸ์ˆ˜ ๋“ฑ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  • volatile ํ‚ค์›Œ๋“œ๋ฅผ ํ†ตํ•ด ๋ฉ”๋ชจ๋ฆฌ ๊ฐ€์‹œ์„ฑ์ด ๋ณด์žฅ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ณ€๊ฒฝํ•œ ๊ฐ’์„ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ฆ‰์‹œ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • compareAndSetState(int expect, int update)์™€ ๊ฐ™์€ CAS ์—ฐ์‚ฐ์„ ํ†ตํ•ด ๋ฝ ํš๋“ ๋ฐ ํ•ด์ œ๋ฅผ ์›์ž์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • ReentrantLock์—์„œ๋Š” ์ด state๊ฐ€ ์žฌ์ง„์ž… ํšŸ์ˆ˜๋กœ๋„ ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.

3.1.3 AQS๋ฅผ ์ƒ์†ํ•  ๋•Œ overrideํ•  method

์ธํ„ฐํŽ˜์ด์Šค, ์ถ”์ƒ ํด๋ž˜์Šค ๋“ฑ์„ ๋ณผ ๋•Œ ์˜ค๋ฒ„๋ผ์ด๋“œํ•  ๋ฉ”์„œ๋“œ๋ฅผ ํ™•์ธํ•˜๋ฉด ์ธํ„ฐํŽ˜์ด์Šค ๋˜๋Š” ์ถ”์ƒํด๋ž˜์Šค์™€ ๊ทธ๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๋Š” ํด๋ž˜์Šค๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

AQS๋Š” ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๋ผ, ๋‚ด๋ถ€ ํ—ฌํผ ํด๋ž˜์Šค๋กœ ์ƒ์†ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์›์น™์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ReentrantLock, Semaphore, CountDownLatch ๋“ฑ์€ ๋ชจ๋‘ ๋‚ด๋ถ€์ ์œผ๋กœ AQS๋ฅผ ์ƒ์†ํ•˜์—ฌ ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค.

Subclasses should be defined as non-public internal helper classes that are used to implement the synchronization properties of their enclosing class.

AQS๋ฅผ ์ƒ์†ํ•˜์—ฌ ๋™๊ธฐํ™” ๋„๊ตฌ๋ฅผ ๊ตฌํ˜„ํ•  ๋•Œ ์˜ค๋ฒ„๋ผ์ด๋“œํ•ด์•ผ ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • tryAcquire(int)
  • tryRelease(int)
  • tryAcquireShared(int)
  • tryReleaseShared(int)
  • isHeldExclusively()

์ด ๋ฉ”์„œ๋“œ๋“ค์€ ๋ชจ๋‘ ๊ธฐ๋ณธ์ ์œผ๋กœ UnsupportedOperationException์„ ๋˜์ง€๋ฉฐ, ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ ๋ฐ˜๋“œ์‹œ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ๋™๊ธฐํ™” ์ƒํƒœ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • getState()
  • setState(int)
  • compareAndSetState(int, int)

Defining these methods is the only supported means of using this class. All other methods are declared final because they cannot be independently varied.

์ฆ‰, AQS๋Š” ์ด ๋‹ค์„ฏ ๊ฐ€์ง€ ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ๋งŒ ๋™์ž‘์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ทธ ์™ธ์˜ ๋ฉ”์„œ๋“œ๋Š” ๋ชจ๋‘ final๋กœ ์„ ์–ธ๋˜์–ด ์žˆ์–ด ๋ณ€๊ฒฝ์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.


3.2 ReentrantLock์€ AQS๋ฅผ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”๊ฐ€

ReentrantLock์€ ๋‚ด๋ถ€์˜ Sync ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด AQS๋ฅผ ์ƒ์†ํ•˜์—ฌ ๋ฝ์˜ ๋ชจ๋“  ๋™์ž‘์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

3.2.1 Sync ํด๋ž˜์Šค ๊ตฌ์กฐ

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;

    abstract static class Sync extends AbstractQueuedSynchronizer {
        // ๊ณตํ†ต ๋™์ž‘ ์ •์˜
    }

    static final class NonfairSync extends Sync {
        // ๋น„๊ณต์ • ๋ฝ ๊ตฌํ˜„
    }

    static final class FairSync extends Sync {
        // ๊ณต์ • ๋ฝ ๊ตฌํ˜„
    }
}
  • ReentrantLock์€ ์ƒ์„ฑ ์‹œ์ ์— ๊ณต์ •์„ฑ ์—ฌ๋ถ€์— ๋”ฐ๋ผ FairSync ๋˜๋Š” NonfairSync๋ฅผ sync์— ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

3.2.2 ๊ณต์ •์„ฑ๊ณผ Sync ์„ ํƒ

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
  • fair = true์ด๋ฉด ํ ์ˆœ์„œ๋ฅผ ์—„๊ฒฉํžˆ ์ง€ํ‚ค๋Š” FairSync, ๊ธฐ๋ณธ๊ฐ’์€ NonfairSync์ž…๋‹ˆ๋‹ค.

3.2.3 ์žฌ์ง„์ž…๊ณผ ๋ฝ ํš๋“ ๋กœ์ง

abstract boolean initialTryLock();

@ReservedStackAccess
final void lock() {
    if (!initialTryLock())
        acquire(1); // AQS์˜ ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
}
static final class NonfairSync extends Sync {
    final boolean initialTryLock() {
        Thread current = Thread.currentThread();
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(current);
            return true;
        } else if (getExclusiveOwnerThread() == current) {
            int c = getState() + 1;
            if (c < 0)
                throw new Error("Maximum lock count exceeded");
            setState(c);
            return true;
        } else {
            return false;
        }
    }
}
  • ๋ฝ์ด ๋น„์–ด ์žˆ์œผ๋ฉด CAS๋กœ ํš๋“
  • ํ˜„์žฌ ๋ฝ ๋ณด์œ ์ž๊ฐ€ ๋‹ค์‹œ lockํ•˜๋ฉด ์žฌ์ง„์ž… ํ—ˆ์šฉ (state++)
  • ๊ทธ ์™ธ์˜ ๊ฒฝ์šฐ์—” ์‹คํŒจํ•˜๊ณ  AQS ํ์— ์ง„์ž…ํ•˜์—ฌ ๋Œ€๊ธฐ

4. ์ฃผ์š” ๋ฉ”์„œ๋“œ ๋ฐ ํ™œ์šฉ ์˜ˆ์‹œ

4.1 ReentrantLock ์ฃผ์š” ๋ฉ”์„œ๋“œ

4.1.1 ๊ธฐ๋ณธ lock() / unlock()

Lock lock = new ReentrantLock();

lock.lock();
try {
    // critical section
} finally {
    lock.unlock();
}
  • lock() ํ˜ธ์ถœ ์‹œ ๋ฝ์„ ์ฆ‰์‹œ ์–ป์„ ์ˆ˜ ์žˆ์œผ๋ฉด ๋ฐ”๋กœ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์•„๋‹ˆ๋ฉด AQS ํ์— ์ง„์ž…ํ•ด ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค.
  • unlock()์€ state๋ฅผ ๊ฐ์†Œ์‹œํ‚ค๊ณ , 0์ด ๋˜๋ฉด ๋‹ค์Œ ๋Œ€๊ธฐ ์ค‘์ธ ์Šค๋ ˆ๋“œ๋ฅผ ๊นจ์›๋‹ˆ๋‹ค.

4.1.2 lockInterruptibly()

try {
    lock.lockInterruptibly();
    try {
        // ์ž‘์—… ์ˆ˜ํ–‰
    } finally {
        lock.unlock();
    }
} catch (InterruptedException e) {
    // ์ธํ„ฐ๋ŸฝํŠธ ์‘๋‹ต ์ฒ˜๋ฆฌ
}
  • synchronized์™€ ๋‹ฌ๋ฆฌ ์ธํ„ฐ๋ŸฝํŠธ์— ์‘๋‹ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ธด ์ž‘์—… ์ค‘ ์ธํ„ฐ๋ŸฝํŠธ ์ฒ˜๋ฆฌ๋ฅผ ์š”๊ตฌํ•˜๋Š” ๊ฒฝ์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

4.1.3 tryLock(long timeout, TimeUnit unit)

if (lock.tryLock(3, TimeUnit.SECONDS)) {
    try {
        // ๋ฝ ํš๋“ ์„ฑ๊ณต ์‹œ ์ˆ˜ํ–‰
    } finally {
        lock.unlock();
    }
} else {
    // ๋ฝ ํš๋“ ์‹คํŒจ
}
  • ์ผ์ • ์‹œ๊ฐ„ ๋™์•ˆ ๋ฝ์„ ์‹œ๋„ํ•˜๋‹ค๊ฐ€ ์‹คํŒจํ•˜๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ต์ฐฉ ์ƒํƒœ ํšŒํ”ผ ๋กœ์ง ๋“ฑ์— ํ™œ์šฉ๋ฉ๋‹ˆ๋‹ค.

4.1.4 Condition ๊ฐ์ฒด

Condition condition = lock.newCondition();

lock.lock();
try {
    while (!์กฐ๊ฑด) {
        condition.await(); // ๋Œ€๊ธฐ
    }
    // ์กฐ๊ฑด ๋งŒ์กฑ โ†’ ์ž‘์—… ์ˆ˜ํ–‰
    condition.signal(); // ์กฐ๊ฑด ๋งŒ์กฑ ์•Œ๋ฆผ
} finally {
    lock.unlock();
}
  • wait/notify์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ, ํ•˜๋‚˜์˜ ๋ฝ์— ์—ฌ๋Ÿฌ Condition์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ณด๋‹ค ์ •๊ตํ•œ ๋™๊ธฐํ™” ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4.2 ๊ณต์ •์„ฑ๊ณผ ๋ฝ ํš๋“ ์ˆœ์„œ: Fair vs NonFair ๋ชจ๋“œ

ReentrantLock์€ ์ƒ์„ฑ ์‹œ ๊ณต์ •์„ฑ ์—ฌ๋ถ€๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

Lock fairLock = new ReentrantLock(true);  // ๊ณต์ • ๋ชจ๋“œ
Lock unfairLock = new ReentrantLock();    // ๋น„๊ณต์ • ๋ชจ๋“œ (๊ธฐ๋ณธ๊ฐ’)

4.2.1 Fair ๋ชจ๋“œ

  • ๋ฝ ์š”์ฒญ์ด ๋จผ์ € ํ์— ๋“ค์–ด์˜จ ์ˆœ์„œ๋Œ€๋กœ (FIFO) ๋ฝ์„ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค.
  • hasQueuedPredecessors()๋ฅผ ํ†ตํ•ด ์ž์‹ ์˜ ์ฐจ๋ก€์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • ์žฅ์ : ๊ณต์ •ํ•œ ๋ฝ ๋ถ„๋ฐฐ
  • ๋‹จ์ : ์ „์ฒด ์ฒ˜๋ฆฌ๋Ÿ‰(throughput)์ด ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ์Œ
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        setState(nextc);
        return true;
    }
    return false;
}

4.2.2 NonFair ๋ชจ๋“œ

  • ๋จผ์ € ๋ฝ์„ ์ฆ‰์‹œ CAS๋กœ ์‹œ๋„ํ•˜๊ณ , ์‹คํŒจํ•  ๊ฒฝ์šฐ์—๋งŒ AQS ํ์— ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค.
  • ํ์˜ ์•ž์— ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์žˆ์–ด๋„ ๋ฝ์„ ๋จผ์ € ์–ป์„ ์ˆ˜ ์žˆ์Œ โ†’ ๊ณต์ •ํ•˜์ง€ ์•Š์Œ.
  • ์žฅ์ : ์ฒ˜๋ฆฌ๋Ÿ‰(throughput)์ด ๋†’๊ณ  context switching ๋น„์šฉ์ด ์ ์Œ
  • ๋‹จ์ : ํŠน์ • ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฝ์„ ์–ป์ง€ ๋ชปํ•˜๋Š” starvation์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ
// ReentrantLock.NonfairSync ๋‚ด๋ถ€์˜ nonfairTryAcquire
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // ํ ์ƒํƒœ ํ™•์ธ ์—†์ด ๋ฐ”๋กœ CAS ์‹œ๋„
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        setState(nextc);
        return true;
    }
    return false;
}

4.2.3 Fair vs NonFair ๋ชจ๋“œ ์„ฑ๋Šฅ ๋น„๊ต ๋ฐ ์ฐจ์ด ๋ฐœ์ƒ ์š”์ธ

โœ… ์ผ๋ฐ˜์ ์ธ ์„ฑ๋Šฅ ๊ฒฝํ–ฅ

ํ•ญ๋ชฉ๊ณต์ • ๋ชจ๋“œ (Fair)๋น„๊ณต์ • ๋ชจ๋“œ (NonFair)
ํ‰๊ท  ์ฒ˜๋ฆฌ๋Ÿ‰ (Throughput)๋‚ฎ์Œ๋†’์Œ
์ง€์—ฐ ์‹œ๊ฐ„ ํŽธ์ฐจ์ž‘์Œํผ
Starvation ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ๋‚ฎ์Œ๋†’์Œ
Lock ํš๋“ ์†๋„๋А๋ฆผ๋น ๋ฆ„

๐Ÿ’ก ์„ฑ๋Šฅ ๋น„๊ต

  • ๊ฒฝ์Ÿ ์Šค๋ ˆ๋“œ ์ˆ˜๊ฐ€ ๋งŽ์„์ˆ˜๋ก NonFair ๋ชจ๋“œ๊ฐ€ ์ข€ ๋” ๋น ๋ฅธ ์ฒ˜๋ฆฌ๋Ÿ‰์„ ๋ณด์ž…๋‹ˆ๋‹ค.
  • ๋‹จ, NonFair ๋ชจ๋“œ๋Š” ํŠน์ • ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋ฝ์„ ์–ป์œผ๋ฉฐ ์ง€์—ฐ์ด ํŽธ์ค‘๋˜๋Š” ํ˜„์ƒ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โš™๏ธ ์„ฑ๋Šฅ ์ฐจ์ด๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š” ํ•ต์‹ฌ ์š”์†Œ๋“ค

  1. ์„ ์  ๊ธฐํšŒ์™€ CAS ์‹œ๋„ ์‹œ์ 

    • NonFair ๋ชจ๋“œ๋Š” compareAndSetState(0, 1)์„ ๋ฐ”๋กœ ์‹œ๋„.
    • Fair ๋ชจ๋“œ๋Š” hasQueuedPredecessors() ์กฐ๊ฑด ํ™•์ธ์œผ๋กœ ๋ถ„๊ธฐ ๋น„์šฉ ์กด์žฌ.
  2. Context Switching ๋นˆ๋„

    • Fair ๋ชจ๋“œ๋Š” ํ ์ˆœ์„œ๋ฅผ ๋ณด์žฅํ•˜๋ ค๋‹ค ๋ณด๋‹ˆ ์Šค๋ ˆ๋“œ ๊ฐ„ ์ „ํ™˜์ด ์ž์ฃผ ๋ฐœ์ƒ.
    • NonFair ๋ชจ๋“œ๋Š” ๋ฝ ์†Œ์œ ์ž๊ฐ€ ๋ฐ”๋กœ ์žฌ์ง„์ž…ํ•˜์—ฌ ์Šค์œ„์นญ ์ตœ์†Œํ™”.
  3. ๋ฝ ํ•ด์ œ ํ›„ ๋‹ค์Œ ์Šค๋ ˆ๋“œ ์„ ํƒ ๋ฐฉ์‹

    • Fair ๋ชจ๋“œ๋Š” unparkSuccessor(head)๋กœ ํ ์•ž์„ ๊นจ์›€.
    • NonFair๋Š” ๋ฝ ์š”์ฒญ์ž๊ฐ€ ์ƒˆ๋กœ ์„ ์  ๊ฐ€๋Šฅ.
  4. JVM ์ตœ์ ํ™” ๋ฐ ์บ์‹œ ์ผ๊ด€์„ฑ

    • Fair๋Š” ์ƒํƒœ ๊ณต์œ ๊ฐ€ ๋งŽ์•„ cache coherence ๋น„์šฉ ์ฆ๊ฐ€.
    • NonFair๋Š” ์ƒํƒœ ๊ณต์œ  ์ ๊ณ , ๊ฒฝ๋กœ ๋‹จ์ˆœ โ†’ JIT ์ตœ์ ํ™” ์œ ๋ฆฌ.

โœ… Fair, NonFair ์„ ํƒ ๊ธฐ์ค€

์ƒํ™ฉ์ถ”์ฒœ ๋ชจ๋“œ
์„ฑ๋Šฅ์ด ๊ฐ€์žฅ ์ค‘์š”ํ•˜๊ณ  ๋ฝ ์ ์œ  ์‹œ๊ฐ„์ด ์งง์€ ๊ฒฝ์šฐNonFair
๊ณต์ •์„ฑ์ด ์ค‘์š”ํ•œ ์˜ˆ์•ฝ/์Šค์ผ€์ค„๋ง ์‹œ์Šคํ…œFair
๋ฝ ํš๋“ ์ˆœ์„œ์— ๋ฏผ๊ฐํ•œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์‹œ์Šคํ…œFair
๋Œ€๋ถ€๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ (๊ธฐ๋ณธ ์„ค์ •)NonFair

5. ReentrantLock ์‚ฌ์šฉ ์‹œ ์Šค๋ ˆ๋“œ ์ƒํƒœ ๋ณ€ํ™”

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

5.1 ์Šค๋ ˆ๋“œ ์ƒํƒœ ์ „์ด ํ๋ฆ„

NEW โ†’ RUNNABLE โ†’ BLOCKED / WAITING / TIMED_WAITING โ†’ RUNNABLE โ†’ TERMINATED
  • lock() ์‹คํŒจ ์‹œ: AQS ๋Œ€๊ธฐ ํ์— ์ง„์ž…ํ•˜๋ฉฐ LockSupport.park()๋ฅผ ํ†ตํ•ด WAITING ๋˜๋Š” TIMED_WAITING ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
  • unlock() ์‹œ: AQS ๋‚ด๋ถ€์—์„œ ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ unpark()ํ•˜์—ฌ ๊นจ์–ด๋‚˜๊ฒŒ ํ•จ โ†’ RUNNABLE๋กœ ์ „ํ™˜
  • Condition.await()๋ฅผ ํ˜ธ์ถœํ•œ ์Šค๋ ˆ๋“œ: WAITING ์ƒํƒœ๋กœ ์ง„์ž…ํ•˜๋ฉฐ, signal() ๋˜๋Š” signalAll() ํ˜ธ์ถœ ์‹œ ๊นจ์–ด๋‚จ

5.2 ์ƒํƒœ ๋ณ€ํ™” ์˜ˆ์‹œ ์‹œ๋‚˜๋ฆฌ์˜ค

Thread A: lock() ์„ฑ๊ณต โ†’ ์ž‘์—… ์ˆ˜ํ–‰
Thread B: lock() ์‹คํŒจ โ†’ AQS ํ ๋Œ€๊ธฐ โ†’ WAITING
Thread A: unlock() โ†’ Thread B ๊นจ์›€ โ†’ RUNNABLE
Thread B: lock() ์žฌ์‹œ๋„ โ†’ ์ž‘์—… ์ˆ˜ํ–‰
  • ์Šค๋ ˆ๋“œ B๋Š” ๋ฝ์„ ์–ป์ง€ ๋ชปํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— AQS ํ์— ๋“ฑ๋ก๋˜๊ณ , ๋‚ด๋ถ€์ ์œผ๋กœ LockSupport.park()๋กœ ์ธํ•ด ๋ธ”๋กœํ‚น๋ฉ๋‹ˆ๋‹ค.
  • ์Šค๋ ˆ๋“œ A๊ฐ€ ๋ฝ์„ ํ•ด์ œํ•˜๋ฉด AQS๋Š” ๋‹ค์Œ ๋…ธ๋“œ(์Šค๋ ˆ๋“œ B)๋ฅผ unpark()ํ•˜์—ฌ ๋‹ค์‹œ ๋ฝ ํš๋“์„ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

5.3 Condition ๋Œ€๊ธฐ ์ƒํƒœ ํ๋ฆ„

ReentrantLock์€ Condition ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด wait/notify์™€ ์œ ์‚ฌํ•œ ์กฐ๊ฑด ๋Œ€๊ธฐ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ์Šค๋ ˆ๋“œ๋Š” ์ผ๋ฐ˜ AQS ํ๊ฐ€ ์•„๋‹Œ ๋ณ„๋„์˜ Condition ๋Œ€๊ธฐ ํ์— ๋“ค์–ด๊ฐ€๋ฉฐ ์ƒํƒœ ์ „์ด ์—ญ์‹œ ๋‹ค๋ฅด๊ฒŒ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.

lock.lock();
try {
    while (!์กฐ๊ฑด) {
        condition.await(); // WAITING ์ƒํƒœ๋กœ ์ง„์ž…
    }
    // ์กฐ๊ฑด ๋งŒ์กฑ ์‹œ ์ž‘์—… ์ˆ˜ํ–‰
} finally {
    lock.unlock();
}
  • await()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ํ˜„์žฌ ๋ฝ์„ ๋ฐ˜๋‚ฉํ•˜๊ณ  Condition ๋Œ€๊ธฐ ํ์— ๋“ค์–ด๊ฐ€๋ฉฐ WAITING ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
  • ์ดํ›„ signal() ํ˜ธ์ถœ ์‹œ ๋‹ค์‹œ AQS ํ๋กœ ์ด๋™ํ•˜์—ฌ lock() ์žฌ์ง„์ž…์„ ์‹œ๋„ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

6. ๋งˆ์น˜๋ฉฐ

synchronized๋Š” ๊ฐ„๋‹จํ•˜๊ณ  ์•ˆ์ •์ ์ธ ๋™๊ธฐํ™” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ณต์žกํ•œ ๋™๊ธฐํ™” ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ ์‹ค๋ฌด ํ™˜๊ฒฝ์—์„œ๋Š” ReentrantLock์ด ํ›จ์”ฌ ๋” ๊ฐ•๋ ฅํ•œ ์„ ํƒ์ง€๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ํŠนํžˆ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์š”๊ตฌ ์‚ฌํ•ญ์ด ์žˆ๋Š” ๊ฒฝ์šฐ์— ํฐ ์žฅ์ ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค:

  • ๊ณต์ •์„ฑ(Fairness): ๋ฝ ํš๋“ ์ˆœ์„œ๋ฅผ ์ œ์–ดํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ (new ReentrantLock(true))
  • ์กฐ๊ฑด ๊ธฐ๋ฐ˜ ๋Œ€๊ธฐ/์•Œ๋ฆผ: Condition์„ ํ†ตํ•ด ์—ฌ๋Ÿฌ ์กฐ๊ฑด์„ ๋…๋ฆฝ์ ์œผ๋กœ ๋‹ค๋ฃจ๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ
  • ์ธํ„ฐ๋ŸฝํŠธ/ํƒ€์ž„์•„์›ƒ ์ฒ˜๋ฆฌ: lockInterruptibly(), tryLock(timeout) ๋“ฑ์œผ๋กœ ์œ ์—ฐํ•œ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ
  • ์žฌ์ง„์ž… ์ถ”์ : state๋ฅผ ํ†ตํ•œ ์žฌ๊ท€์  ๋ฝ ํš๋“ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ

ReentrantLock์˜ ๋‚ด๋ถ€ ๊ตฌํ˜„์€ AQS(AbstractQueuedSynchronizer)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ๋†’์€ ์ˆ˜์ค€์˜ ์ œ์–ด์™€ ํ™•์žฅ ๊ฐ€๋Šฅ์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํ•œ lock-unlock์„ ๋„˜์–ด์„œ ์Šค๋ ˆ๋“œ ์ƒํƒœ ์ œ์–ด, ์กฐ๊ฑด ๋Œ€๊ธฐ, ๊ณต์ •์„ฑ ๋ณด์žฅ ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์œ ์—ฐํ•˜๊ฒŒ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ํฐ ์žฅ์ ์ž…๋‹ˆ๋‹ค.

profile
ํ–‰๋ณต๊ณผ ๊ฐ™์€ ์†๋„๋ฅผ ์ฐพ๋Š” ๊ฐœ๋ฐœ์ž

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

comment-user-thumbnail
2025๋…„ 6์›” 17์ผ

์ข‹์€์ •๋ณด๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ํ˜น์‹œ ์ถœ์ฒ˜๋Š” ์–ด๋””์ธ์ง€์•Œ์ˆ˜์žˆ์„๊นŒ์š”?

1๊ฐœ์˜ ๋‹ต๊ธ€