HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
📝
남득윤 학습 저장소
/
🧵
멀티쓰레드, 동시성 프로그래밍
/
🧵
Java Concurrency and Multithreding
/
Java Lock

Java Lock

Java Lock and ReentrantLock introduction

  • 자바에서 (동시성) 락은 인터페이스 이다.
  • 쓰레드의 접근을 제어하고 연산의 원자성을 보장하기 위해 사용된다.
  • 동시성 블럭과 비슷하지만 더욱 flexible한 기능을 제공한다.
 
notion image
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 the Lockinstance if possible. If the Lock  instance is already locked, the thread calling lock()is blocked until the Lockis unlocked.
  • lockInterruptibly()
    • The lockInterruptibly() method locks the Lock unless the thread calling the method has been interrupted. Additionally, if a thread is blocked waiting to lock the Lock via this method, and it is interrupted, it exits this method calls.
    • 호출하는 쓰레드가 interrupt 되지 않은 경우 lock()처럼 동작, 그렇지 않다면 InterruptedException 발생 (CheckedException)
  • tryLock()
    • The tryLock() method attempts to lock the Lock instance immediately. It returns true if the locking succeeds, false if Lock is already locked. This method never blocks.
    • 락이 가능하면 락, true반환/ 락이 불가능하면 false반환
  • tryLock(long timeout, TimeUnit timeUnit)
    • The tryLock(long timeout, TimeUnit timeUnit) works like the tryLock() method, except it waits up the given timeout before giving up trying to lock the Lock .
  • unlock()
    • The unlock() method unlocks the Lock instance. Typically, a Lock implementation will only allow the thread that has locked the Lock 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 return true if that Thread is queued up waiting to lock the Lock, and false if not.
  • hasQueuedThreads()
    • The ReentrantLock hasQueuedThreads() method returns true if any threads are queued up waiting to lock this Lock, and false if not.
  • isFair()
    • The ReentrantLock isFair() method returns true if this Lock guarantees fairness among threads waiting to lock it, and false if not.
  • isHeldByCurrentThread()
    • The ReentrantLock isHeldByCurrentThread() method returns true if the Lock is held (locked) by the thread calling isHeldByCurrentThread(), and false if not.
  • isLocked()
    • The ReentrantLock isLocked() method returns true if the Lock is currently locked, and false if not.
 

Java Lock vs. synchronized blocks - differences and similarities.

  1. 동기화 블럭은한 메서드 내에서만 사용 가능한 반면 lock.lock()과 lock.unlock()의 호출은 다른 메서드에서도 가능
 
  1. lock.lock()과 lock.unlock()은 동기화 블럭과 같은 visibility와 happens before guarantees를 제공함
 
  1. 동기화 블럭은 항상 reentrant 하다. Lock은 그렇지 않을 수도 있음
 
  1. 동기화 블럭은 fainess를 제공할 수 없다. Lock은 제공할 수 있다.