
Java์ ๋ฉํฐ์ค๋ ๋ฉ ํ๊ฒฝ์์ ๋๊ธฐํ๋ ํ์์
๋๋ค. ๊ฐ์ฅ ํํ ์ฌ์ฉํ๋ synchronized ์ธ์๋, java.util.concurrent.locks ํจํค์ง์ ํฌํจ๋ ReentrantLock์ ๋ณด๋ค ์ ๊ตํ ์ ์ด๋ฅผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค. ์ด ๊ธ์์๋ ReentrantLock์ synchronized์ ๋น๊ตํ๋ฉฐ, ๊ทธ ๋ด๋ถ ์๋ฆฌ์ ์ด๋ ํ ํน์ง์ด ์๋์ง ์์๋ณด๊ฒ ์ต๋๋ค.
๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์์ ๊ณต์ ์์์ ๋ณดํธํ๊ธฐ ์ํ ๊ฐ์ฅ ๋ํ์ ์ธ ๋ฐฉ๋ฒ์ synchronized์
๋๋ค. ํ์ง๋ง ๋ณด๋ค ์ ์ฐํ๊ณ ํ์ฅ ๊ฐ๋ฅํ ๋ฝ์ด ํ์ํ ๋๋ ReentrantLock์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ ํฉํฉ๋๋ค. ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ ์ฐจ์ด์ ์ ๊ฐ์ง๋๋ค
| ํญ๋ชฉ | synchronized | ReentrantLock |
|---|---|---|
| ์ ์ธ ๋ฐฉ์ | ํค์๋ ์ฌ์ฉ (synchronized) | ๊ฐ์ฒด ์์ฑ ํ ๋ช ์์ lock/unlock |
| ๊ณต์ ์ฑ(Fairness) ์ค์ | ๋ถ๊ฐ๋ฅ | ๊ฐ๋ฅ (new ReentrantLock(true)) |
| ์ธํฐ๋ฝํธ ๋์ | ๋ถ๊ฐ๋ฅ | ๊ฐ๋ฅ (lockInterruptibly()) |
| ํ์์์ ์ค์ | ๋ถ๊ฐ๋ฅ | ๊ฐ๋ฅ (tryLock(timeout)) |
| Condition ์ง์ | wait/notify | Condition ๊ฐ์ฒด ์ฌ์ฉ |
| ์ฝ๋ ์ ์ฐ์ฑ | ์ ํ์ | ๋์ (lock์ ์ฌ๋ฌ ์์น์์ ์ ์ด ๊ฐ๋ฅ) |
synchronized๋ ๊ฐ๊ฒฐํ๊ณ ์ง๊ด์ ์ด์ง๋ง, ์ธ๋ฐํ ๋๊ธฐํ ์ ์ด์๋ ํ๊ณ๊ฐ ์์ต๋๋ค.ReentrantLock์ ๋ช
์์ ์ธ lock/unlock ์ ์ด์ ๋ค์ํ ํ์ฅ ๊ธฐ๋ฅ(๊ณต์ ์ฑ, ์ธํฐ๋ฝํธ, ํ์์์, Condition ๋ฑ)์ ์ ๊ณตํ์ฌ ๋ณต์กํ ๋๊ธฐํ ์๊ตฌ์ ๋์ํ ์ ์์ต๋๋ค.ReentrantLock์ ๋ด๋ถ์ ์ผ๋ก AbstractQueuedSynchronizer(AQS)๋ผ๋ ํ๋ ์์ํฌ๋ฅผ ํ์ฉํด ๋ฝ ํ๋๊ณผ ํด์ ๋ฅผ ๊ตฌํํฉ๋๋ค. ์ฌ๊ธฐ์์๋ AQS์ ๊ฐ๋
๊ณผ, ReentrantLock์ด ์ด๋ฅผ ์ด๋ป๊ฒ ํ์ฅํ์ฌ ์ฌ์ฉํ๋์ง ์์ธํ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
AbstractQueuedSynchronizer๋ ๋ฝ, ์ธ๋งํฌ์ด ๋ฑ ๋ค์ํ ๋๊ธฐํ ๊ตฌ์กฐ์ ๊ธฐ๋ฐ์ด ๋๋ ํด๋์ค์
๋๋ค.
์ค์ ๋ก ReentrantLock ์ด์ธ์๋ Semaphore, CountDownLatch, ReentrantReadWriteLock ๋ฑ์์ AQS๋ฅผ ํ์ฉํ๊ณ ์์ต๋๋ค.
ํต์ฌ์ int state ์ํ๊ฐ๊ณผ FIFO ๋ฐฉ์์ ๋๊ธฐ ํ๋ฅผ ํตํด ์ค๋ ๋ ๊ฐ ์ ๊ทผ์ ์ ์ดํ๋ค๋ ์ ์
๋๋ค.
acquire(), release() ๋ฑ ํ
ํ๋ฆฟ ๋ฉ์๋๋ก ๋๊ธฐํ ํ๋ฆ์ ์ ์tryAcquire(), tryRelease() ๋ฑ ๋ฉ์๋๋ฅผ ํ์ ํด๋์ค์์ ๊ตฌํํ๋๋ก ์์LockSupport.park()/unpark()๋ก ๋ธ๋กํน ์ ์ด์ค๋ ๋๋ค์ด FIFO ์์๋ก ์ฐ๊ฒฐ๋ฆฌ์คํธ์ ๋ถ๊ณ , ์ ๋ ธ๋์ ์ ํธ์ ์ํด ๋ฝ์ ์์ฐจ์ ์ผ๋ก ํ๋ํจ์ผ๋ก์จ,
๊ณต์ ์ฑ(fairness)๊ณผ ์บ์ ํจ์จ์ฑ(locality)์ ๋์์ ๋ฌ์ฑํ๋ ๋ฝ ๋๊ธฐ ๋ฉ์ปค๋์ฆ์ด๋ค.
// AbstractQueuedSynchronizer.java
abstract static class Node {
volatile Node prev;
volatile Node next;
Thread waiter;
volatile int status;
...
}
๋ฝ ์ํ ํ์ธ ์ ์ ๋ ธ๋์ ์ํ(status)๋ง ํ์ธ
์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์ผํ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์(CAS ๋์ ํ๋)๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ์ฝ๊ณ ์ด๋ค๋ฉด?
์บ์ ํจ์จ์ฑ์ ๋ฌ์ฑํ๋ ๋ฉ์ปค๋์ฆ
| ๋ฉ์ปค๋์ฆ | ์ค๋ช |
|---|---|
| ์ฝ๊ธฐ ์ ์ฉ ๊ฐ์ ๊ตฌ์กฐ | ์ค๋ ๋๋ ๋๋ถ๋ถ prev.status๋ง ์ฝ๊ธฐ ์ ์ฉ ์ ๊ทผ โ ์บ์ ํํธ์จ โ |
| ์ฐ๊ธฐ ์ต์ํ | ๋๊ธฐ์ด ์ง์ ์ tail์๋ง CAS โ ๊ณต์ ํ๋ ์ฐ๊ธฐ ๊ฐ์, ์บ์ ๊ฒฝํฉ ์ต์ํ |
| ๋ ธ๋ ๊ฐ ๋ ๋ฆฝ์ ๋ฉ๋ชจ๋ฆฌ ๋ฐฐ์น | ๊ฐ ์ค๋ ๋๋ ์์ ๋ง์ ๋ ธ๋๋ฅผ ์ฌ์ฉํ๋ฉฐ, ๋ค๋ฅธ ์ค๋ ๋์ ๋์ผํ ์บ์๋ผ์ธ์ ๊ณต์ ํ์ง ์์ โ false sharing ๋ฐฉ์ง |
| LockSupport ๊ธฐ๋ฐ ๋ธ๋กํน | ์คํ์ ์ต์ํํ์ฌ ์บ์๋ผ์ธ invalidation ๋น์ฉ ๊ฐ์ |
private volatile int state;
volatile int state์ด๋ฉฐ, ๋ฝ์ ๋ณด์ ์ฌ๋ถ๋ ์ฌ์ง์
ํ์ ๋ฑ์ ๋ํ๋
๋๋ค.volatile ํค์๋๋ฅผ ํตํด ๋ฉ๋ชจ๋ฆฌ ๊ฐ์์ฑ์ด ๋ณด์ฅ๋ฉ๋๋ค. ์ฆ, ํ ์ค๋ ๋๊ฐ ๋ณ๊ฒฝํ ๊ฐ์ ๋ค๋ฅธ ์ค๋ ๋๊ฐ ์ฆ์ ๋ณผ ์ ์์ต๋๋ค.compareAndSetState(int expect, int update)์ ๊ฐ์ CAS ์ฐ์ฐ์ ํตํด ๋ฝ ํ๋ ๋ฐ ํด์ ๋ฅผ ์์์ ์ผ๋ก ์ํํฉ๋๋ค.ReentrantLock์์๋ ์ด state๊ฐ ์ฌ์ง์
ํ์๋ก๋ ํ์ฉ๋ฉ๋๋ค.์ธํฐํ์ด์ค, ์ถ์ ํด๋์ค ๋ฑ์ ๋ณผ ๋ ์ค๋ฒ๋ผ์ด๋ํ ๋ฉ์๋๋ฅผ ํ์ธํ๋ฉด ์ธํฐํ์ด์ค ๋๋ ์ถ์ํด๋์ค์ ๊ทธ๊ฒ์ ๊ตฌํํ๋ ํด๋์ค๊ฐ์ ๊ด๊ณ๋ฅผ ํ์ ํ ์ ์์ต๋๋ค.
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๋ก ์ ์ธ๋์ด ์์ด ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅํฉ๋๋ค.
ReentrantLock์ ๋ด๋ถ์ Sync ํด๋์ค๋ฅผ ํตํด AQS๋ฅผ ์์ํ์ฌ ๋ฝ์ ๋ชจ๋ ๋์์ ๊ตฌํํฉ๋๋ค.
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์ ํ ๋นํฉ๋๋ค.public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
fair = true์ด๋ฉด ํ ์์๋ฅผ ์๊ฒฉํ ์งํค๋ FairSync, ๊ธฐ๋ณธ๊ฐ์ NonfairSync์
๋๋ค.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;
}
}
}
state++)Lock lock = new ReentrantLock();
lock.lock();
try {
// critical section
} finally {
lock.unlock();
}
lock() ํธ์ถ ์ ๋ฝ์ ์ฆ์ ์ป์ ์ ์์ผ๋ฉด ๋ฐ๋ก ๋ฐํํ๊ณ , ์๋๋ฉด AQS ํ์ ์ง์
ํด ๋๊ธฐํฉ๋๋ค.unlock()์ state๋ฅผ ๊ฐ์์ํค๊ณ , 0์ด ๋๋ฉด ๋ค์ ๋๊ธฐ ์ค์ธ ์ค๋ ๋๋ฅผ ๊นจ์๋๋ค.try {
lock.lockInterruptibly();
try {
// ์์
์ํ
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
// ์ธํฐ๋ฝํธ ์๋ต ์ฒ๋ฆฌ
}
synchronized์ ๋ฌ๋ฆฌ ์ธํฐ๋ฝํธ์ ์๋ตํ ์ ์์ต๋๋ค.if (lock.tryLock(3, TimeUnit.SECONDS)) {
try {
// ๋ฝ ํ๋ ์ฑ๊ณต ์ ์ํ
} finally {
lock.unlock();
}
} else {
// ๋ฝ ํ๋ ์คํจ
}
Condition condition = lock.newCondition();
lock.lock();
try {
while (!์กฐ๊ฑด) {
condition.await(); // ๋๊ธฐ
}
// ์กฐ๊ฑด ๋ง์กฑ โ ์์
์ํ
condition.signal(); // ์กฐ๊ฑด ๋ง์กฑ ์๋ฆผ
} finally {
lock.unlock();
}
wait/notify์ ์ ์ฌํ์ง๋ง, ํ๋์ ๋ฝ์ ์ฌ๋ฌ Condition์ ๋ง๋ค ์ ์์ต๋๋ค.ReentrantLock์ ์์ฑ ์ ๊ณต์ ์ฑ ์ฌ๋ถ๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค:
Lock fairLock = new ReentrantLock(true); // ๊ณต์ ๋ชจ๋
Lock unfairLock = new ReentrantLock(); // ๋น๊ณต์ ๋ชจ๋ (๊ธฐ๋ณธ๊ฐ)
hasQueuedPredecessors()๋ฅผ ํตํด ์์ ์ ์ฐจ๋ก์ธ์ง ํ์ธํฉ๋๋ค.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;
}
// 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;
}
| ํญ๋ชฉ | ๊ณต์ ๋ชจ๋ (Fair) | ๋น๊ณต์ ๋ชจ๋ (NonFair) |
|---|---|---|
| ํ๊ท ์ฒ๋ฆฌ๋ (Throughput) | ๋ฎ์ | ๋์ |
| ์ง์ฐ ์๊ฐ ํธ์ฐจ | ์์ | ํผ |
| Starvation ๋ฐ์ ๊ฐ๋ฅ์ฑ | ๋ฎ์ | ๋์ |
| Lock ํ๋ ์๋ | ๋๋ฆผ | ๋น ๋ฆ |
์ ์ ๊ธฐํ์ CAS ์๋ ์์
compareAndSetState(0, 1)์ ๋ฐ๋ก ์๋.hasQueuedPredecessors() ์กฐ๊ฑด ํ์ธ์ผ๋ก ๋ถ๊ธฐ ๋น์ฉ ์กด์ฌ.Context Switching ๋น๋
๋ฝ ํด์ ํ ๋ค์ ์ค๋ ๋ ์ ํ ๋ฐฉ์
unparkSuccessor(head)๋ก ํ ์์ ๊นจ์.JVM ์ต์ ํ ๋ฐ ์บ์ ์ผ๊ด์ฑ
| ์ํฉ | ์ถ์ฒ ๋ชจ๋ |
|---|---|
| ์ฑ๋ฅ์ด ๊ฐ์ฅ ์ค์ํ๊ณ ๋ฝ ์ ์ ์๊ฐ์ด ์งง์ ๊ฒฝ์ฐ | NonFair |
| ๊ณต์ ์ฑ์ด ์ค์ํ ์์ฝ/์ค์ผ์ค๋ง ์์คํ | Fair |
| ๋ฝ ํ๋ ์์์ ๋ฏผ๊ฐํ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์์คํ | Fair |
| ๋๋ถ๋ถ์ ์ ํ๋ฆฌ์ผ์ด์ (๊ธฐ๋ณธ ์ค์ ) | NonFair |
ReentrantLock์ AQS๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค๋ ๋ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ์, ๋ฝ ํ๋ ์คํจ, ์กฐ๊ฑด ๋๊ธฐ ๋ฑ ์ํฉ์ ๋ฐ๋ผ JVM์ ์ค๋ ๋ ์ํ๊ฐ ๋ค์ํ ๋ฐฉ์์ผ๋ก ๋ณํํฉ๋๋ค. ์ด ์ฅ์์๋ ์ฃผ์ ์ํ ์ ์ด ํ๋ฆ๊ณผ ์์๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
NEW โ RUNNABLE โ BLOCKED / WAITING / TIMED_WAITING โ RUNNABLE โ TERMINATED
lock() ์คํจ ์: AQS ๋๊ธฐ ํ์ ์ง์
ํ๋ฉฐ LockSupport.park()๋ฅผ ํตํด WAITING ๋๋ TIMED_WAITING ์ํ๊ฐ ๋ฉ๋๋ค.unlock() ์: AQS ๋ด๋ถ์์ ๋ค์ ๋
ธ๋๋ฅผ unpark()ํ์ฌ ๊นจ์ด๋๊ฒ ํจ โ RUNNABLE๋ก ์ ํCondition.await()๋ฅผ ํธ์ถํ ์ค๋ ๋: WAITING ์ํ๋ก ์ง์
ํ๋ฉฐ, signal() ๋๋ signalAll() ํธ์ถ ์ ๊นจ์ด๋จThread A: lock() ์ฑ๊ณต โ ์์
์ํ
Thread B: lock() ์คํจ โ AQS ํ ๋๊ธฐ โ WAITING
Thread A: unlock() โ Thread B ๊นจ์ โ RUNNABLE
Thread B: lock() ์ฌ์๋ โ ์์
์ํ
LockSupport.park()๋ก ์ธํด ๋ธ๋กํน๋ฉ๋๋ค.unpark()ํ์ฌ ๋ค์ ๋ฝ ํ๋์ ์๋ํ ์ ์๊ฒ ํฉ๋๋ค.ReentrantLock์ Condition ๊ฐ์ฒด๋ฅผ ํตํด wait/notify์ ์ ์ฌํ ์กฐ๊ฑด ๋๊ธฐ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ ์ค๋ ๋๋ ์ผ๋ฐ AQS ํ๊ฐ ์๋ ๋ณ๋์ Condition ๋๊ธฐ ํ์ ๋ค์ด๊ฐ๋ฉฐ ์ํ ์ ์ด ์ญ์ ๋ค๋ฅด๊ฒ ๋ํ๋ฉ๋๋ค.
lock.lock();
try {
while (!์กฐ๊ฑด) {
condition.await(); // WAITING ์ํ๋ก ์ง์
}
// ์กฐ๊ฑด ๋ง์กฑ ์ ์์
์ํ
} finally {
lock.unlock();
}
await()๋ฅผ ํธ์ถํ๋ฉด ํ์ฌ ๋ฝ์ ๋ฐ๋ฉํ๊ณ Condition ๋๊ธฐ ํ์ ๋ค์ด๊ฐ๋ฉฐ WAITING ์ํ๊ฐ ๋ฉ๋๋ค.signal() ํธ์ถ ์ ๋ค์ AQS ํ๋ก ์ด๋ํ์ฌ lock() ์ฌ์ง์
์ ์๋ํ๊ฒ ๋ฉ๋๋ค.synchronized๋ ๊ฐ๋จํ๊ณ ์์ ์ ์ธ ๋๊ธฐํ ๋๊ตฌ์
๋๋ค. ํ์ง๋ง ๋ณต์กํ ๋๊ธฐํ ์ ์ด๊ฐ ํ์ํ ์ค๋ฌด ํ๊ฒฝ์์๋ ReentrantLock์ด ํจ์ฌ ๋ ๊ฐ๋ ฅํ ์ ํ์ง๊ฐ ๋ฉ๋๋ค. ํนํ ๋ค์๊ณผ ๊ฐ์ ์๊ตฌ ์ฌํญ์ด ์๋ ๊ฒฝ์ฐ์ ํฐ ์ฅ์ ์ ๊ฐ์ง๋๋ค:
new ReentrantLock(true))Condition์ ํตํด ์ฌ๋ฌ ์กฐ๊ฑด์ ๋
๋ฆฝ์ ์ผ๋ก ๋ค๋ฃจ๊ณ ์ถ์ ๊ฒฝ์ฐlockInterruptibly(), tryLock(timeout) ๋ฑ์ผ๋ก ์ ์ฐํ ์ ์ด๊ฐ ํ์ํ ๊ฒฝ์ฐstate๋ฅผ ํตํ ์ฌ๊ท์ ๋ฝ ํ๋ ๊ด๋ฆฌ๊ฐ ํ์ํ ๊ฒฝ์ฐReentrantLock์ ๋ด๋ถ ๊ตฌํ์ AQS(AbstractQueuedSynchronizer)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฉฐ, ์ด๋ฅผ ํตํด ๋์ ์์ค์ ์ ์ด์ ํ์ฅ ๊ฐ๋ฅ์ฑ์ ์ ๊ณตํฉ๋๋ค. ๋จ์ํ lock-unlock์ ๋์ด์ ์ค๋ ๋ ์ํ ์ ์ด, ์กฐ๊ฑด ๋๊ธฐ, ๊ณต์ ์ฑ ๋ณด์ฅ ๋ฑ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ์ฐํ๊ฒ ์ ๊ณตํ๋ ๊ฒ์ด ํฐ ์ฅ์ ์
๋๋ค.
์ข์์ ๋ณด๊ฐ์ฌํฉ๋๋ค! ํน์ ์ถ์ฒ๋ ์ด๋์ธ์ง์์์์๊น์?