Java

모니터와 락에 대하여

Andrew-Yun 2022. 3. 10. 19:52

자바에선 자체적으로 동기화 메커니즘을 지원한다. C언어와는 달리 언어 자체에서 지원하는 잠금이 있는건 특이했다.

사전지식

잠금 방식에 따른 구분

  • Busy - Wait: CPU 자원을 소모하면서 락을 얻을 때까지 대기함.
  • Block and Wakeup: 임계 영역에 들어가면, 프로세스 상태가 wait 상태로 변하며, 자원을 얻을 때까지 대기함.

모니터란 ?

  • 모니터란 자바에서 지원하는 락 방법 중 하나이다. synchronized 키워드를 붙여 구현할 수 있으며, 클래스, 메서드에 붙일 수 있다. 각각을 살펴보면, 클래스에 붙인 경우 인스턴스 자체가 동기화되므로, 오직 하나의 스레드만이 인스턴스에 접근할 수 있다. 메서드에 붙인 경우 해당 메서드를 실행할 때 오직 하나의 스레드만이 실행할 수 있다.
  • 동작 방식은 Block and Wakeup이다.

하지만 오직 하나의 스레드만 실행 가능하기 때문에 둘 이상의 스레드는 실행 불가능하다는 단점이 있다. (카운팅 세마포어)

상세 구현 메커니즘

동기화 문제의 대표적인 예시인 생산자 소비자 문제를 모니터로 구현한 예시를 보겠다.

 

버퍼가 비어있는지 꽉 차있는지에 따라 이벤트 시그널을 발생시켜 다른 스레드들을 깨운다.

Lock 인터페이스란?

lock 인터페이스는 자바에서 락을 구현하기 위한 인터페이스이다.

public interface Lock {
    void lock();

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();

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

    void unlock();

    Condition newCondition();
}

메서드 이름에 따라 폴링, 타임아웃 등의 락 메커니즘을 구현해야 하며, lock, unlock으로 명시적으로 잠금을 얻고, 해제한다. 구현체는 주로 ReentantLock 클래스 등이 있다.

Blocking / Non-blocking차이점이란?

blocking이란 I/O 등의 작업이 끝날 때까지 기다려야 하는 이벤트가 발생하면, 해당 작업이 끝날 때까지 코드 수행을 멈추고 다른 프로세스를 실행하다가, 작업이 완료되면 다시 코드를 수행하는 메커니즘이다. 또한 함수를 실행시키면 제어권이 함수로 넘어가고 해당 함수가 끝날 때까지 대기하다가 다시 제어권을 받아 실행한다.

non-blocking이란 이벤트가 발생하면, 커널에 제어권을 맡긴 뒤 작업이 완료되면 이벤트를 발생시켜, 이벤트 루프라는 메인 스레드에서 해당 이벤트가 발생된 시점에서 완료된 로직을 처리하게 된다. 함수도 이벤트로 취급할 수 있다.

왜 논블로킹을 사용할까?

프로세스의 작업 타입엔 두 가지가 있다. CPU를 많이 쓰는 타입과, I/O가 많이 발생하는 타입이다. 후자는 I/O가 끝날 때 까지 CPU를 사용할 수 없기 때문에 cpu utilization 측면에서 좋지 않다.

따라서, I/O를 하는 동안 최대한 CPU를 사용하기 위해 이벤트 루프라는 개념을 도입했다. 블로킹 방식과의 차이점은 제어권을 넘기지 않고 작업을 수행하는 책임만 넘기는 것으로 봐도 좋다.

다른 언어 에서의 사례

Javascript

fetch 함수의 결과를 then으로 감싼 부분에서 받고, 람다 함수의 파라미터로 전달받아 콘솔에 출력하는 코드이다.

//fetch api
fetch("https://jsonplaceholder.typicode.com/posts/1").then((response) =>
  console.log(response)
);