고유 락(Intrinsic Lock)
고유 락은 자바의 동기화 방법 중 하나로, 객체 단위로 동기화를 제어하는 데 사용된다. 모든 자바 객체는 내부적으로 고유한 락을 가지고 있다. 이 락은 synchronized 키워드를 사용하여 블록 또는 메서드 단위로 획득하고 해제할 수 있다. 이를 통해 한 번에 하나의 스레드 만이 해당 락을 획득하여 블록 내의 코드를 실행할 수 있다.
synchronized
'synchronized' 키워드는 고유 락을 이용하여 스레드 간의 동기화와 상호 배제를 구현
public class SynchronizedExample {
private int count = 0;
// 1.
public synchronized int increase() {
return ++count;
}
// 2.
public int increase() {
synchronized(this) {
return ++count;
}
}
}
재진입 가능성(Reentrancy)
락을 획득한 스레드가 같은 락을 얻기 위해 대기할 필요가 없다. 왜냐하면 락의 획득이 호출 단위가 아니라 스레드 단위로 일어나기 때문이다.
아래 코드를 보면 'outer()' 메서드 내에서 'inner()' 메서드를 호출하며, 이 두 메서드는 모두 같은 락인 'lock'을 사용하여 동기화 된다. 이렇게 되면 'outer()' 에서 락을 획득한 상태에서 'inner()' 를 호출해도 데드락이 발생하지 않는다. = 재진입 가능성
public class ReentrancyExample {
private Object lock = new Object();
public void outer() {
synchronized (lock) {
System.out.println("Outer Lock Acquired");
inner(); // 같은 락 내에서 또 다른 synchronized 메서드 호출
}
}
public void inner() {
synchronized (lock) {
System.out.println("Inner Lock Acquired");
}
}
}
구조적 락(Structured Lock)
synchronized를 이용한 동기화 = 구조적인 락
synchronized 블록을 통해 락의 획득과 해제가 코드의 구조에 따라 자동으로 이루어지기 때문에 락의 획득 및 해제를 개발자가 명시적으로 처리하지 않아도 된다. 즉, 구조적 락 A와 B가 있을 때 A획득 → B획득 → B해제 → A해제는 가능하지만 A획득 → B획득 → A해제 → B해제는 불가능하다.
명시적 락(ReentrantLock)
구조적 락에서는 불가능했던 A획득 → B획득 → A해제 → B해제를 가능하게 해준다.
public class ReentrantLockExample {
private int count = 0;
private Lock lock = new ReentrantLock(); // 명시적인 락 생성
public void increment() {
lock.lock(); // 락 획득
try {
count++;
} finally {
lock.unlock(); // 락 해제
}
}
}
명시적 락은 읽기와 쓰기 작업을 모두 동일한 락으로 관리하므로 읽기 작업도 락을 획득해야 한다. 이로 인해 읽기 작업이 동시에 여러 스레드에서 수행되더라고 락 경합이 발생하여 성능 저하가 발생할 수 있다.
이때, 성능을 향상시키기 위해 Read/Write Lock을 사용할 수 있다.
Read/Write Lock
Read/Write Lock은 락을 읽기 락과 쓰기 락으로 분리하여 관리한다. 따라서 여러 스레드가 동시에 읽기 락을 획득할 수 있으며, 읽기 락이 획득되어 있는 동안에는 쓰기 락을 획득할 수 없다. 즉, 읽기 락이 획득되어 있으면 다른 스레드도 읽기 락을 획득할 수 있지만, 쓰기 락을 획득할 수는 없으므로 데이터 일관성을 보장한다. 그리고 쓰기 락이 획득되어 있으면 어떤 스레드도 읽기 락이나 쓰기 락을 획득할 수 없다.
다만, 쓰기 작업이 빈번하거나 긴 시간동안 계속될 경우 성능 저하가 발생할 수 있다.
public class ReadWriteLockExample {
private int value = 0;
private ReadWriteLock lock = new ReentrantReadWriteLock();
public int getValue() {
lock.readLock().lock(); // 읽기 락 획득
try {
return value;
} finally {
lock.readLock().unlock(); // 읽기 락 해제
}
}
public void setValue(int newValue) {
lock.writeLock().lock(); // 쓰기 락 획득
try {
value = newValue;
} finally {
lock.writeLock().unlock(); // 쓰기 락 해제
}
}
}
'Languages > Java' 카테고리의 다른 글
[Java] Garbage Collection (0) | 2023.09.06 |
---|---|
[Java] JVM (Java Virtual Machine) (0) | 2023.09.06 |
[Java] 스레드 (Thread) (0) | 2023.08.31 |
[Java] Casting (0) | 2023.08.10 |
[Java] 문자열 클래스 & 오브젝트 클래스 (0) | 2023.08.10 |