Java ‐ 고급 동기화 concurrent.Lock - dnwls16071/Backend_Study_TIL GitHub Wiki

📚 LockSupport(저수준)

  • synchronized 단점 정리
    • 무한 대기 : BLOCKED 상태 쓰레드는 락을 획득한 쓰레드가 락을 해제할 때까지 무한 대기한다.
      • 특정 시간까지만 대기하는 타임아웃X
      • 중간에 인터럽트 발생 X
    • 공정성 : 락이 해제된 후 BLOCKED 상태 쓰레드 중에서 어떤 쓰레드가 락을 획득했는지 알 수 없다. 최악의 경우 특정 쓰레드가 너무 오랜기간 동안 락을 획득하지 못할 수 있다.

❗이런 문제를 해결하기 위해 동시성 문제 해결을 위한 라이브러리 패키지가 추가되었다. ❗LockSupport를 사용하면 synchronized의 가장 큰 단점인 무한 대기 문제를 해결할 수 있다.

📚 ReentrantLock❗

비공정모드(Non-fair mode)

  • 비공정모드는 ReentrantLock의 기본 모드이다. 이 모드에서는 락을 먼저 요청한 쓰레드가 먼저 획득한다는 보장이 없다.
  • 락을 해제할 때 대기 중인 쓰레드 중 아무나 락을 획득할 수 있다.
  • 이는 락을 빨리 획득할 수 있지만, 특정 쓰레드가 장기간 락을 획득하지 못할 수도 있다.
  • 성능 특징 : 락을 획득하는 속도가 빠르다, 새로운 쓰레드가 기존 쓰레드보다 먼저 락을 선점할 수 있다, 특정 쓰레드가 계속해서 락을 획득하지 못할 수 있다.

공정모드(Fair mode)

  • 생성자에서 true를 전달하면 된다.
  • 공정 모드는 락을 요청한 순서대로 쓰레드가 락을 획득할 수 있다.
  • 이는 먼저 대기한 쓰레드가 먼저 락을 획득하게 되면서 쓰레드 간의 공정성을 보장한다. 그러나 이로 인해 성능이 저하될 수 있다.
  • 성능 특징 : 대기 큐에서 먼저 대기한 쓰레드가 락을 먼저 획득한다, 모든 쓰레드가 언젠가는 락을 획득할 수 있도록 보장한다, 락을 획득하는 속도가 느려질 수 있다.

❗비공정 모드는 성능을 중시하고 쓰레드가 락을 빨리 획득할 수 있지만 특정 쓰레드가 계속해서 락을 획득하지 못할 수 있다. ❗공정 모드는 쓰레드가 락을 획득하는 순서를 보장하여 공정성을 중시하지만 성능이 저하될 수 있다.

📚 ReentrantLock 예제

public class BankAccount {

	private int balance;
	private final Lock lock = new ReentrantLock();

	public BankAccount(int balance) {
		this.balance = balance;
	}

	public boolean withdraw(int amount) throws InterruptedException {
		lock.lock();	// Reentrant Lock으로 락을 걸기

		try {
			if (balance < amount) {
				return false;
			}

			Thread.sleep(1000);
			balance -= amount;
		} finally {
			lock.unlock();	// 락 해제
		}
		return true;
	}

	public int getBalance() {
		return balance;
	}
}
  • ReentrantLock이 제공하는 락은 객체 내부의 모니터 락이 아니다.
  • ReentrantLock 내부에는 락과 락을 얻지 못해 대기하는 쓰레드를 관리하는 대기 큐가 존재한다.

📚 ReentrantLock 대기 중단

  • ReentrantLock을 사용하면 락을 무한 대기하지 않고 중간에 빠져나오는 것이 가능하다.
  • 심지어 락을 얻을 수 없다면 기다리지 않고 즉시 빠져나오는 것도 가능하다.
  • boolean tryLock() : 락 획득을 시도하고 즉시 성공 여부를 반환한다. 만약 다른 쓰레드가 이미 락을 획득했다면 false를 반환하고 그렇지 않으면 true를 반환한다.
  • boolean tryLock(long time, TimeUnit unit) : 주어진 시간 동안 락 획득을 시도한다. 주어진 시간 안에 락을 획득했다면 true를 반환하고 주어진 시간이 지나도 락을 획득하지 못한 경우 false를 반환한다. 이 메서드는 대기 중 인터럽트가 발생하면 InterruptedException이 발생하고 락 획득을 포기한다.