반응형
개요
- 김영한 강사님의 강의를 듣고 정리하였다.
본론
자바는 1.5버전 부터 Lock 인터페이스와 ReentrantLock 구현체를 제공한다.
- 이는 LockSupport 기능을 활용하여 만들어졌다.
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
- Lock 인터페이스는 synchronized를 대체하여 안전한 임계 영역을 위한 락 기능을 구현하는데 사용되며 대표적인 구현체로는 ReentrantLock이 있다.
- 여기서 사용하는 락은 인스턴스 내부의 모니터락이 아닌 ReentrantLock이 제공해주는 기능 중에 하나로 둘을 다른 것이라고 생각해야 한다.
void lock()
: 락 획득을 시도한다. 만약 락이 존재하지 않는다면 락을 획득할 때 까지 스레드는 WAITING 상태로 들어간다.void lockInterruptibly()
: 락 획득을 시도한다. 만약 락이 존재하지 않는다면 락을 획득할 때까지 스레드는 WAITING 상태로 들어간다.- lock()과 다른 점은 다른 스레드의 인터럽트에 반응한다는 점이다. InterruptedException을 던진다.
boolean tryLock()
: 락 획득을 시도한다. 만약 락이 존재하지 않는다면 false를 반환하고 락을 획득한다면 true를 반환한다.- 이 메서드는 대기하지 않는다.
boolean tryLock(long time, TimeUnit unit)
: 락 획득을 시도한다. 만약 지정된 시간 안에 락을 획득한다면 true를 반환하고, 획득하지 못한다면 false를 반환한다.- InterruptedException을 던진다.
Condition newCondition
: Condition 객체를 생성해 반환한다.
이 기능들을 사용하면 LockSupport의 기능들을 이용한 락 기능을 쉽게 사용할 수 있다.
- 이를 통해 synchronized의 인터럽트, 무한 대기 문제가 해결된다.
공정성
ReentrantLock은 스레드들의 락 획득을 공정하게 획득할 수 있도록 해주는 기능을 제공한다.
- synchronized를 이용한 락 기능에서는 어떤 스레드가 락을 획득하는지 알 수 없었다.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockEx {
// 비공정 모드
private final Lock nonFairLock = new ReentrantLock();
// 공정 모드
private final Lock fairLock = new ReentrantLock(true);
}
new ReentrantLock()
어떤 스레드가 락을 획득하는지 알 수 없다. 대기 중인 스레드 중 아무나 락을 획득한다.
- 비교적 빠르게 락을 획득하지만, 기아 현상이 발생할 수 있다. (특정 스레드가 락을 획득하지 못함)
new ReentrantLock(true)
락을 요청한 순서대로 락을 획득한다.
- 비교적 느리게 락을 획득하지만 순서대로 락을 획득할 수 있어 기아 현상이 발생하지 않는다.
ReentrantLock 사용
@Override
public boolean withdraw(int amount) {
log("거래 시작 : " + getClass().getSimpleName());
lock.lock();
try{
if (balance < amount) {
return false;
}
sleep(1000); // 출금 시간
balance -= amount;
}
finally {
lock.unlock();
}
log("거래 종료");
return true;
}
- lock()메서드와 unlock() 메서드 사이의 임계 영역은 안전하게 보호된다.
@Override
public boolean withdraw(int amount) {
log("거래 시작 : " + getClass().getSimpleName());
if(!lock.tryLock()){
// if(!lock.tryLock(500, TimeUnit.MILLISECONDS)){
return false;
}
try{
if (balance < amount) {
return false;
}
sleep(1000); // 출금 시간
balance -= amount;
}
finally {
lock.unlock();
}
log("거래 종료");
return true;
}
- tryLock() 메서드를 사용해서 락 획득에 실패하면 즉시 종료하거나, 락 획득을 일정 시간까지만 대기하도록 할 수 있다.
결론
ReentrantLock을 사용해 synchronized의 인터럽트, 무한 대기, 공정성 부족 문제를 해결한 락 기능을 사용할 수 있었다.
반응형
'JAVA' 카테고리의 다른 글
[JAVA] 생산자 소비자 문제와 자바의 Object.wait() notify() (2) | 2024.10.02 |
---|---|
[JAVA] java.util.concurrent.LockSupport 알아보자 (0) | 2024.09.24 |
[JAVA] 자바의 임계 영역과 동기화, synchronized (0) | 2024.09.23 |
[JAVA] 자바의 volatile 키워드와 메모리 가시성 (1) | 2024.09.23 |
[JAVA] 스레드의 양보 Thread.yield() (0) | 2024.09.21 |