Java Lock and ReentrantLock introduction
- 자바에서 (동시성) 락은 인터페이스 이다.
- 쓰레드의 접근을 제어하고 연산의 원자성을 보장하기 위해 사용된다.
- 동시성 블럭과 비슷하지만 더욱 flexible한 기능을 제공한다.

public class LockExample { public static void main(String[] args) { Lock lock = new ReentrantLock(); lock.lock(); //do something lock.unlock(); } }
Two concurrent Counter class examples
public class CounterSynchronized { private long count = 0; public synchronized void inc(){ this.count++; } public synchronized long getCount(){ return this.count; } }
public class CounterLock { private long count = 0; private Lock lock = new ReentrantLock(); public synchronized void inc(){ try{ lock.lock(); this.count++; }finally { lock.unlock(); } } public synchronized long getCount(){ try{ lock.lock(); return this.count; }finally { lock.unlock(); } } }
Lock and unlock the Lock inside a try-finally clause
Counterlock
클래스에서 try-finally 블락으로 lock, unlock 메서드를 실행 한 것을 확인 할 수 있다. 예외가 발생하여도 unlock을 보장할 수 있다.Lock reentrance
-
ReentrantLock
은 재 진입 할 수 있다.
- 즉, 락을 한번 가진 쓰레드가 계속 락을 걸 수 있다.
- 락을 풀기 위해서는 락을 건만큼 풀어야한다.
한 쓰레드가 락을 여러번 물고 놓지 않아 생기는 deadlock을 reentrance lockout이라고 한다.
Lock reentrance use case
public class Calculator { public static class Calculation { public static final int UNSPECIFIED = -1; public static final int ADDITION = 0; public static final int SUBTRACTION = 1; int type = UNSPECIFIED; public double value; public Calculation(int type, double value){ this.type = type; this.value = value; } } private double result = 0.0D; Lock lock = new ReentrantLock(); public void add(double value) { try { lock.lock(); this.result += value; } finally { lock.unlock(); } } public void subtract(double value) { try { lock.lock(); this.result -= value; } finally { lock.unlock(); } } public void calculate(Calculation ... calculations) { try { lock.lock(); for(Calculation calculation : calculations) { switch(calculation.type) { case Calculation.ADDITION : add (calculation.value); break; case Calculation.SUBTRACTION: subtract(calculation.value); break; } } } finally { lock.unlock(); } } }
calculate
메서드를 호출하면calcaulate
메서드에서 한번,add
메서드에서 한번 총 두번 락을 물고 놓는다.
Lock fairness
- 기본적으로
ReentranceLock
은 쓰레드 간의 fairness를 제공하지 않는다. - 즉 lock을 대기중인 여러 쓰레드가 있다면 lock 이 풀린경우 아무 쓰레드나 준다.
ReentranceLock
의 생성자에 true 파라미터를 주면 오래 대기한 쓰레드에 먼저 lock을 제공하는 fairness를 보장할 수 있다.
- fairness를 제공하지 않으면 starvation 상태에 이를 수 있다.
- 하지만 fairness를 제공하는 것은 약간의 오버헤드가 있다.
Lock examples
The Java Lock interface contains the following primary methods:
- lock()
- The
lock()
method locks theLock
instance if possible. If theLock
instance is already locked, the thread callinglock()
is blocked until theLock
is unlocked.
- lockInterruptibly()
- The
lockInterruptibly()
method locks theLock
unless the thread calling the method has been interrupted. Additionally, if a thread is blocked waiting to lock theLock
via this method, and it is interrupted, it exits this method calls. - 호출하는 쓰레드가 interrupt 되지 않은 경우
lock()
처럼 동작, 그렇지 않다면InterruptedException
발생 (CheckedException)
- tryLock()
- The
tryLock()
method attempts to lock theLock
instance immediately. It returnstrue
if the locking succeeds, false ifLock
is already locked. This method never blocks. - 락이 가능하면 락,
true
반환/ 락이 불가능하면false
반환
- tryLock(long timeout, TimeUnit timeUnit)
- The
tryLock(long timeout, TimeUnit timeUnit)
works like thetryLock()
method, except it waits up the given timeout before giving up trying to lock theLock
.
- unlock()
- The
unlock()
method unlocks theLock
instance. Typically, aLock
implementation will only allow the thread that has locked theLock
to call this method. Other threads calling this method may result in an unchecked exception (RuntimeException
).
Lock.lockInterruptibly()
Lock.tryLock()
ReentrantLock methods
The Java ReentrantLock also has a few interesting public methods:
- getHoldCount()
- The Java ReentrantLock
getHoldCount()
method returns the number of times a given thread has locked this Lock instance. A thread can lock a Lock more than once due to Lock reentrance.
- getQueueLength()
- The ReentrantLock
getQueueLength()
method returns the number of threads waiting to lock the Lock.
- hasQueuedThread(Thread)
- The ReentrantLock
hasQueuedThread(Thread thread)
method takes a Thread as parameter and returntrue
if that Thread is queued up waiting to lock the Lock, andfalse
if not.
- hasQueuedThreads()
- The ReentrantLock
hasQueuedThreads()
method returnstrue
if any threads are queued up waiting to lock this Lock, andfalse
if not.
- isFair()
- The ReentrantLock
isFair()
method returnstrue
if this Lock guarantees fairness among threads waiting to lock it, andfalse
if not.
- isHeldByCurrentThread()
- The ReentrantLock
isHeldByCurrentThread()
method returnstrue
if the Lock is held (locked) by the thread callingisHeldByCurrentThread()
, andfalse
if not.
- isLocked()
- The ReentrantLock
isLocked()
method returnstrue
if the Lock is currently locked, andfalse
if not.
Java Lock vs. synchronized blocks - differences and similarities.
- 동기화 블럭은한 메서드 내에서만 사용 가능한 반면
lock.lock()
과lock.unlock()
의 호출은 다른 메서드에서도 가능
lock.lock()
과lock.unlock()
은 동기화 블럭과 같은 visibility와 happens before guarantees를 제공함
- 동기화 블럭은 항상 reentrant 하다.
Lock
은 그렇지 않을 수도 있음
- 동기화 블럭은 fainess를 제공할 수 없다.
Lock
은 제공할 수 있다.