티스토리 뷰

반응형

자바는 멀티 스레드 프로그래밍을 지원하는 언어이다. 그렇다 보니 아무 스레드가 와서 마음껏 자원을 읽고 쓰고가 가능하다면 클라이언트가 원하는 정보를 얻기 힘들 것이다. 그렇기 때문에 자바에서는 Lock을 걸어 공유 자원이 Thread-safe 하게 해준다.

그렇다면 자바에서는 어떻게 Lock을 걸 수 있을까?

Monitor(모니터)

모니터는 고급 언어의 설계 구조물로서, 개발자의 코드를 상호배제 하게끔 만든 추상화된 데이터 형태이다. 세마포의 소유와 획득 과정을 모니터 자체 내에서 처리하기 때문에 코딩하기에 조금 더 편리하며 잘못된 코딩으로 인한 문제 발생이 세마포보다 덜 하다.

또한 모니터는 세마포의 block/wakeup과 유사한 wait/signal을 제공한다. 세마포는 리스트 형태로 사용하지만 모니터는 condition을 정하고, 컨디션과 wait/signal의 조합을 통해 프로세스의 상태가 결정된다. wait 상태의 프로세스는 suspend 상태가 된다.

자바에선 모든 객체는 monitor를 갖고 있고, monitor는 여러 스레드가 객체로 동시에 접근하는 것을 막는다.

여기서 모든 객체에 주목해보자, 모든 객체라 함은 heap영역에서 공유되는 인스턴스들을 말하는데 이 때 heap영역은 모든 쓰레드에게 공유되는 공유자원이다. 그렇기 때문에 스레드가 monitor를 가지면, monitor를 갖는 객체에 Lock을 걸 수 있다. 그렇게 되면 다른 thread들이 해당 객체에 접근이 제한되기 때문에 외부 다른 Thread로부터 안전하게 작업을 할 수 있다.

public class Hello implements Runnable {

    @Override
    public void run() {
        String str = "hello";
        synchronized (str) {
            System.out.println(str);
        }
    }
}​

다음 과 같이 sychronized 키워드 내에서 가능하다.

여기서 hello라는 String 객체가 synchronized에 파라미터로 들어가면서, thread가 hello 객체의 monitor를 가질 수 있게 된다.

Lock과 ReentrantLock

Lock 인터페이스는 암묵적인 락과 달리 조건 없는 락, 폴링 락, 타임아웃이 있는 락 등 대기 상태에 인터럽트를 걸 수 있는 방법 등이 포함돼 있으며, 락을 확보하고 해제하는 모든 작업이 명시적이다.

public interface Lock {
    void lock();

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();

    boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException;

    void unlock();

    Condition newCondition();
}

ReentrantLock 클래스도 마찬가지로 Lock을 구현하여 synchronized 키워드와 동일한 메모리 가시성과 상호 배제 기능을 제공한다. 결국 synchronized 블록에 진입하는 것과 동일하고 재진입 또한 가능하다.

암묵적인 락은 락을 확보하고자 대기하고 있는 상태의 스레드에는 인터럽트 거는 일이 불가능하고, 대기 상태에 들어가지 않으면서 락을 확보하는 방법 등이 꼭 필요한 상황을 지원할 수 없다. 또한 synchronized 블록이 끝나는 시점에 반드시 해제되도록 돼 있는데, 이런 구조는 코딩하기에 간편하고 예외 처리 루틴과 잘 맞아 떨어지는 구조이긴 하지만 블록의 구조를 갖추지 않은 상황에서 락을 걸어야 하는 경우에는 적용하기가 불가능하다.

명시적인 락은 일부 상황에서 성능과 활동성을 높이기 위해 유연성이 높은 락 방법을 제공해준다.

sychronized를 사용하는 암묵적인 락과는 좀 복잡한 규칙이 있는데 바로 finally를 이용해서 락을 반드시 해제 해줘야 한다. 그렇지 않으면 내부에 예외가 발생한다면 락이 해제가 되지 않는다.

public class Counter {

    Lock lock = new ReentrantLock();
    private int count = 0;

    public int inc() {
        lock.lock();
        int newCount = count ++;
        lock.unlock();
        return newCount;
    }
}

ReentrantLock은 다음과 같이 사용할 수 있다.

 

다음 주차에 Synchronized와의 차이점에 대해 알아보도록하고 오늘은 여기서 설명을 마치겠다.

 

Reference.

https://aroundck.tistory.com/3318

 

[Java Concurrency] 명시적인 락

13.1. Lock 과 ReentrantLock - Lock 인터페이스는 암묵적인 락과 달리 조건 없는(unconditional)락, 폴링 락, 타임아웃이 있는 락, 락 확보 대기 상태에 인터럽트를 걸 수 있는 방법 등이 포함돼 있으며, 락을

aroundck.tistory.com

https://parkcheolu.tistory.com/24

 

자바의 락(Locks in Java)

이 글은 원 저자 Jakob Jenkov의 허가로 포스팅된 번역물이다. 자바 컨커런시와 자바 메모리 모델에 관한 자료를 찾던 중 발견한 이 튜토리얼의 깔금한 이미지와 예제, 명료한 설명에 반하여 번역-

parkcheolu.tistory.com

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함