스터디/Java 스터디

[Java] 뮤텍스, 세마포어, 모니터

제이온 (Jayon) 2021. 12. 30.

java-study에서 스터디를 진행하고 있습니다.

뮤텍스 (Mutual Exclusion)

멅리 프로그래밍 환경에서 자원에 대한 접근에 제한을 강제하기 위한 동기화 기법이다. 특징은 다음과 같다.

 

  • Boolean 타입의 Lock 변수를 사용한다. 따라서 1개의 공유 자원에 대한 접근을 제한한다.
  • 공유 자원을 사용 중인 스레드가 있을 때, 다른 스레드가 공유 자원에 접근한다면 블로킹 후 대기 큐로 보낸다.
  • Lock을 건 스레드만 Lock을 해제할 수 있다.

 

세마포어

멀티 프로그래밍 환경에서 다수의 프로세스나 스레드가 n개의 공유 자원에 대한 접근을 제한하는 방법으로 사용되는 동기화 기법이다.

 

  • 세마포어 변수를 통해 wait, signal을 관리한다. 세마포어 변수는 0 이상의 정수형 변수를 갖는다.
  • n개의 공유 자원에 대한 접근을 제한할 수 잇으며, 이를 계수 세마포어라고 한다.
  • 접근 가능한 공유 자원의 수가 1개일 때는 이진 세마포어로 뮤텍스와 유사하게 사용할 수 있다.
  • 큐에 연결된 스레드를 깨우는 방식에 따라 강성 세마포어(큐에 연결된 스레드를 깨울 때 FIFO 정책), 약성 세마포어(큐에 연결된 스레드를 깨울 때 순서를 특별히 명시하지 않음)로 구분된다.
  • 세마포어는 Signaling 메커니즘으로 Lock을 걸지 않은 스레드도 Signal을 보내 Lock을 해제할 수 있다.

 

세마포어로 동시 접근 제어

synchronized의 경우 오직 하나의 스레드만 수행 가능하다면, 세마포어는 동시에 실행할 수 있는 스레드의 수를 제어할 수 있다. 다음은 동시에 수행 중인 스레드의 번호를 출력하는 예제이다.

 

public class Resource {

    private final Semaphore semaphore;
    private final int maxThread;

    public Resource(int maxThread) {
        this.maxThread = maxThread;
        this.semaphore = new Semaphore(maxThread);
    }

    public void use() {
        try {
            semaphore.acquire();
            System.out.println("[" + Thread.currentThread().getName() + "]" + (maxThread - semaphore.availablePermits())
                + "개의 스레드가 점유중");
            Thread.sleep(1000);
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

Resource 인스턴스의 use() 메소드를 maxThread 수만큼 스레드가 동시에 호출 가능하도록 만들었다. use() 메소드를 호출하면 세마포어를 몇 개이 스레드가 점유 중인지 출력하고 1초 간 sleep을 호출한다. 실제로 스레드를 생성해 동기화가 잘 지켜지는지 확인해 보자.

 

ublic class Semaphore {

    public static void main(String[] args) {
        Resource resource = new Resource(3);
        for (int i = 1; i <= 10; i++) {
            Thread t = new Thread(resource::use);
            t.start();
        }
    }
}

 

실행 결과는 다음과 같다.

 

[Thread-1]2개의 스레드가 점유중
[Thread-2]3개의 스레드가 점유중
[Thread-0]2개의 스레드가 점유중
[Thread-3]1개의 스레드가 점유중
[Thread-5]3개의 스레드가 점유중
[Thread-4]2개의 스레드가 점유중
[Thread-6]2개의 스레드가 점유중
[Thread-8]3개의 스레드가 점유중
[Thread-7]2개의 스레드가 점유중
[Thread-9]1개의 스레드가 점유중

 

세마포어를 maxThread에 설정한 최대 스레드 수인 3개 이하로만 스레드가 점유하는 것을 확인할 수 있다. 이렇게 세마포어를 처리하는 경우 동시에 수행되는 스레드의 수를 제어하는 것이 가능하다.

(참고로 Java에는 뮤텍스 라이브러리가 없으므로 비슷하게 구현하고 싶다면, 이진 세마포어를 사용해야 한다.)

 

모니터

뮤텍스와 세마포어는 상호 배제를 위한 동기화 개념이다. 그러나 완벽한 상호 배제를 제공한다고 할 수 없기 때문에 이를 보완한 모니터를 사용하면 훨씬 쉽게 동기화를 사용할 수 있다.

세마포어는 wait & signal 연산 순서를 바꿔서 실행하거나 둘 중 하나라도 생략하면 상호 배제를 위반하는 상황이어서 교착 상태가 발생한다. 만약 wait & signal 연산이 프로그램 전체에 구성되어 있으면 세마포어의 영향이 미치는 곳이 어딘지 파악하기 파악하기 어렵기 때문에 세마포어를 사용해 프로그램을 구현하는 것은 매우 어렵다. 이런 단점을 극복하기 위해 모니터가 등장했고, 이는 프로그래밍 언어 수준에서 제공된다. 대표적으로 Java가 있다.

 

모니터는 임계 구역을 지켜내기 위한 방법인 상호 배제를 프로그램으로 구현한 것이며, 이진 세마포어만 가능하다. 다음은 모니터의 구조를 간단히 나타낸 그림이다.

 

 

모니터는 공유 자원, 공유 자원 접근 함수로 이루어져 있고, 2개의 큐를 가지고 있다.

 

  • 상호 배타 큐 (Mutual Exclusion Queue)
    • 공유 자원에 하나의 프로세스만 진입하도록 하기 위한 큐이다. 공유 자원을 사용하는 스레드가 존재하면 상호 배타 큐에 존재한다.
  • 조건 동기 큐 (Conditional Synchronization Queue)
    • 공유 자원의 Lock 이 해제되기를 기다리는 스레드가 대기하는 큐이다.

 

모니터를 통해 프로세스가 자원에 접근하는 방식을 이미지로 보면 아래와 같다.

 

 

공유 자원을 점유 중인 프로세스(스레드)는 Lock을 가지고 있다. 공유 자원을 점유 중인 프로세스(스레드)가 있는 상황에서 다른 프로세스(스레드)가 공유 자원에 접슨하려고 하면 외부 모니터 조건 동기 큐(준비 큐)에서 진입을 기다린다.

 

synchronized

Java의 synchronized 키워드는 스레드 동기화를 할 때 사용되는 대표적인 기법이다. 자바의 모든 인스턴스는 모니터를 가지고 있으며, 모니터를 통해 스레드 동기화를 수행한다.

모니터의 라이프 사이클은 synchronized 키워드에 의존하기 때문에, 인스턴스가 가지는 wait() , notify() , notifyAll() 메소드는 모두 synchronized 블록에서만 유의미하다.

 

wait(), notify(), notifyAll()

모니터에는 조건 변수가 있는데, 이를 통해 wait(), notify(), notifyAll() 메소드가 구현되어 있다.

 

  • wait()
    • Lock을 가진 스레드가 다른 스레드에 Lock을 넘겨준 다음 대기해야 할 때 사용한다.
  • notify()
    • 대기하고 있는 스레드 중 하나를 깨운다.
  • notifyAll()
    • 대기하고 있는 스레드 모두를 깨운다.

 

출처

 

예상 면접 질문 및 답변

뮤텍스에 대해 설명하라

여러 스레드를 실행하는 환경에서 자원에 대한 접근에 제한을 강제하기 위한 동기화 기법이며 다음과 같은 특징이 있다.

 

  1. Boolean 타입의 Lock 변수를 사용한다. 따라서 1개의 공유자원에 대한 접근을 제한한다.
  2. 공유자원을 사용 중인 스레드가 있을 때, 다른 스레드가 공유자원에 접근한다면 Blocking 후 대기 큐로 보낸다.
  3. Lock을 건 스레드만 Lock을 해제할 수 있다.

 

세마포어에 대해 설명하라

멀티 프로그래밍 환경에서 다수의 프로세스나 스레드가 n개의 공유 자원에 대한 접근을 제한하는 방법으로 사용되는 동기화 기볍이며 다음과 같은 특징이 있다.

 

  1. 세마포어 변수를 통해 wait, signal을 관리한다. 세마포어 변수는 0 이상의 정수형 변수를 갖는다.
  2. n 개의 공유자원에 대한 접근을 제한할 수 있으며 이를 계수 세마포어라고 한다.
  3. 접근 가능한 공유 자원의 수가 1개일 때는 이진 세마포어로 뮤텍스처럼 사용할 수 있다.
  4. 큐에 연결된 스레드를 깨우는 방식에 따라 강성 세마포어(큐에 연결된 스레드를 깨울 때 FIFO 정책), 약성 세마포어(큐에 연결된 스레드를 깨울 때 순서를 특별히 명시하지 않음)로 구분된다.
  5. 세마포어는 Signaling 메커니즘으로 Lock을 걸지 않은 스레드도 Signal을 보내 Lock을 해제할 수 있다.

 

세마포어의 단점

세마포어는 wait & signal 연산 순서를 바꿔서 실행하거나 둘 중 하나라도 생략하면 교착 상태가 발생하고, wait & signal 연산이 프로그램 전체에 구성되어 있으면 세마포어의 영향이 미치는 곳이 어딘지 파악하기 어렵기 때문에 세마포어를 사용해 프로그램을 구현하는 것은 매우 어렵다. 이런 단점을 극복하기 위해 모니터가 등장했다.

 

모니터에 대해 설명

모니터는 임계 구역을 지켜내기 위한 방법인 상호 배제를 프로그램으로 구현한 것이며, 공유 자원, 공유 자원 접근함수와 두 개의 큐로 구성되어 있다. 공유 자원을 점유 중인 프로세스(스레드)는 Lock을 가지고 상호베타 큐에 대기하며, 공유 자원의 Lock이 해제되기를 기다리는 프로세스(스레드)는 조건동기 큐에서 대기한다.

자바에서 동기화를 위해 모니터 방식을 채택하고 있다. synchronized를 임계 영역을 정해 공유 자원의 스레드 간의 동기화를 할 수 있고 모니터의 공유 자원 접근 함수(wait(), notify(), notifyAll() )를 통해 Lock을 관리할 수 있다.

'스터디 > Java 스터디' 카테고리의 다른 글

[Java] atomic과 CAS 알고리즘  (1) 2022.01.06
[Java] 가변 객체 vs 불변 객체  (1) 2021.12.31
[Java] synchronized 키워드란?  (1) 2021.12.29
[Java] volatile 키워드란?  (0) 2021.12.29
[Java] Java의 동시성 이슈  (0) 2021.12.29

댓글

추천 글