[java] 자바 잠금(Java locking)

자바에서는 멀티스레드 환경에서의 동기화를 위해 잠금(Locking) 메커니즘을 제공합니다. 잠금은 스레드 간의 경쟁 조건(race condition)을 방지하고, 스레드들이 안전하게 공유 자원에 접근할 수 있도록 합니다. 자바에서는 synchronized 키워드와 Lock 인터페이스를 이용하여 잠금을 구현할 수 있습니다.

1. synchronized

synchronized 키워드는 메서드 또는 블록 단위에서 사용할 수 있습니다. synchronized 메서드를 사용하는 경우, 해당 메서드는 객체 레벨에서 잠금을 얻습니다. 다른 스레드에서는 해당 메서드를 실행할 수 없고, 대기하게 됩니다.

public synchronized void synchronizedMethod() {
    // 잠금을 얻은 스레드만 실행할 수 있는 코드
}

synchronized 블록을 사용하는 경우, 블록 내부의 임의의 객체를 잠금으로 사용할 수 있습니다. 이를 통해 더 세밀한 제어가 가능하며, 임계 영역(critical section)을 정의할 수 있습니다.

public void synchronizedBlock() {
    synchronized (lockObject) {
        // 잠금을 얻은 스레드만 실행할 수 있는 코드
    }
}

2. Lock 인터페이스

Java 5부터 Lock 인터페이스가 도입되었습니다. Lock 인터페이스를 사용하면 더 세밀한 스레드 동기화를 구현할 수 있습니다. Lock 인터페이스를 구현한 클래스로는 ReentrantLock이 가장 많이 사용됩니다.

Lock lock = new ReentrantLock();

public void lockMethod() {
    lock.lock();
    try {
        // 잠금을 얻은 스레드만 실행할 수 있는 코드
    } finally {
        lock.unlock();
    }
}

Lock 인터페이스를 이용하면 잠금을 얻을 때 timeout 값을 설정할 수도 있습니다. 또한, tryLock() 메서드를 이용하면 잠금을 얻지 못했을 때 다른 작업을 수행할 수도 있습니다.

if (lock.tryLock()) {
    try {
        // 잠금을 얻은 스레드만 실행할 수 있는 코드
    } finally {
        lock.unlock();
    }
} else {
    // 다른 작업 수행
}

3. 잠금의 선택

synchronized 키워드와 Lock 인터페이스 중 어떤 것을 선택해야 할까요?

synchronized는 자바에서 기본적으로 제공되며, 사용하기 간편합니다. 하지만 synchronized 메서드를 사용하는 경우, 잠금을 사용하는 코드의 범위가 큰 단위가 됩니다. 이는 스레드 경합이 많은 상황에서 성능 저하를 일으킬 수 있습니다.

반면에 Lock 인터페이스를 사용하면 잠금을 더 세밀하게 사용할 수 있습니다. 잠금의 범위를 사용자가 직접 지정할 수 있으며, 더 다양한 옵션을 제공합니다. 하지만 좀 더 복잡한 사용법을 익히고, 명시적으로 잠금을 해제해야 합니다.

따라서 스레드 동기화를 선택할 때는 상황에 맞게 synchronized 또는 Lock 인터페이스를 선택해야 합니다.

4. 결론

자바에서는 스레드 동기화를 위해 synchronized 키워드와 Lock 인터페이스를 제공합니다. synchronized 키워드는 간단하게 사용할 수 있지만, 범위가 크고 세밀한 제어가 불가능한 단점이 있습니다. Lock 인터페이스는 더 다양한 세부 설정이 가능하지만, 사용법이 좀 더 복잡합니다.

스레드 동기화는 멀티스레드 환경에서 발생할 수 있는 문제를 방지하기 위해 중요한 요소입니다. 적절한 스레드 동기화를 구현하여 안정적이고 효율적인 멀티스레드 애플리케이션을 개발하는 것이 중요합니다.