Computer Science/[ OS ]

[ OS ] 08. 프로세스 동기화 - 모니터(Monitor)

kim.svadoz 2021. 5. 30. 10:15
반응형

모니터

세마포어나 뮤텍스를 잘못 사용해서 일어나는 에러가 발생할 수 있다.

따라서 자바에서는 더욱 심플한 동기화 툴인 "모니터"를 제공한다.

  • 고급 언어의 설계 구조물로서, 개발자의 코드를 상호배제 하게끔 만든 추상화된 데이터 형태이다.
  • 공유자원에 접근하기 위한 키 획득과 자원 사용 후 해제를 모두 처리한다. (세마포어는 직접 키 해제와 공유자원 접근 처리가 필요하다.

- 모니터의 개념

  • 하나의 데이터(객체)마다 하나의 모니터를 결합할 수 있으며, 모니터는 그것이 결합된 데이터(객체)가 동시에 두 개 이상의 스레드에 의해 접근할 수 없도록 막는 잠금(lock)기능을 제공함으로써, 동기화를 수행하는 동기화 도구이다.
  • 즉, 데이터(객체)에 모니터를 결합하면 하나의 스레드가 그 데이터를 사용하는 동안에는 다른 스레드들이 그 데이터를 사용할 수 없게 된다.
  • 자바에서는 synchronized 메소드가 선언된 객체와 synchronized 블럭에 의해 동기화 되는 모든 객체에 고유한 모니터가 결합이 되어 동기화 작업을 수행한다.

- 모니터의 구성

  • 스레드 단위로 모니터락을 획득(acquire lock)하거나 반환(release lock)한다.
  • 동기화 코드(동기화메소드나 블럭)을 수행할 때에는 동기화 대상 인스턴스와 결합된 Monitor Lock을 획득한 후에 집입이 가능하며, 동기화 코드를 벗어날 때에는 Monitor Lock을 반환한다.
  • 동기화 댕상 인스턴스 별로 이와 결합된 Monitor가 존재하며 해당 모니터는 현재 락을 획득한 스레드와 Lock Count 정보를 관리한다.
  • 모니터가 Lock Count정보를 유지한다는 것은 동일 스레드가 중복해서 lcok을 걸 수 있다는 의미이다.

- 상호배제 임계영역 구현

  • 모니터 타입은 상호배제를 제공해주는 ADT이다. 쉽게 말해서 클래스이다.

    • 여기서 ADTAbstract Data Type 으로 추상 자료형이다.
    • 객체지향의 클래스와 같이 기능의 구현 부분을 나타내지 않고, 데이터의 형태와 그 데이터의 연산들을 정의 해놓은 자료형이다.
    • 우리가 알고 있는 자료구조(Data Structure)는 추상 자료형이 정의한 연산들을 구현한 구현체를 가리킨다.
    • 즉, 추상 자료형은 구현 방법을 명시하고 있지 않다0는 점에서 자료구조와 다르다.
    • 자바로 치면 클래스인지 인터페이스인지 확인하면 된다.
    • 스택이나 큐는 구현 방법이 전혀 정의되어 있지 않으니 추상자료형이고, 배열은 연속적으로 저장되어 있도록 구현되어 있어야 하므로 자료구조이며, 연결리스트도 다음 데이터의 위치를 저장하는 방식으로 정해져 있으니 자료구조이다.
  •   synchronized block {
          // 임계영역에 해당하는 코드 블럭을 선언할 때 사용한다.
          // 해당 임계영역에는 모니터 락을 획득해야 진입할 수 있다.
          // 모니터 락을 가진 객체 인스턴스를 지정할 수 있다.
          // 메소드에 선언하면 메소크 블록 전체가 임계영역으로 지정된다.
          // 이 때 모니터락을 가진 객체 인스턴스는 this 객체 인스턴스이다.
      }
    
      synchronized (object) {
          // critical section
      }
  • 지금 까지 어렵게 상호배제를 구현한 것을, JAVA VM은 위와 같이 심플하게 끝낼 수 있다.

  • 여기까지는 임계영역을 선언한 것이고 동기화는 되지 않은 것이다.

- 동기화 구현

  • 동기화를 하기 위해선 wait()notify() 메소드가 필요하다.
  • 위 메소드는 java.lang.Object Class에 선언 되었다 (이 말은 모든 자바 객체가 가진 메소드이다.)
  • 쓰레드가 어떤 객체의 wait() 메소드를 호출하면 해당 객체의 모니터락을 획득하기 위해 대기 상태로 진입한ㄷ.
  • 쓰레드가 어떤 객체의 notify() 메소드를 호출하면 해당 객체의 모니터에 대기중인 쓰레드 하나를 깨운다.
  • notify() 대신 notifyAll()을 사용하면 해당 객체 모니터에 대기중인 모든 쓰레드를 깨운다.
static class Counter {
    public static int count = 0;
    synchronized public static void increment() {
        // critical section
        count++;
    }
}

/* 이렇게 method 앞에 modifed로 선언하는 것이 현하긴 하지만 내가 정하고 싶은 블록만 동기화 하기 위해선  이렇게도 가능하다. */
private static Object object = new Object();
synchronized (object) {
    count++;
}

/* 여기서 this는 자기 참조 변수 이기 때문에 각각의 쓰레드의 this를 참조 하는 것이다. */
/* -> 이 말은 모니터가 다 따로 존재한다는 의미로, 쓰레드 끼리 독립적이다. */
/* -> 이는 static으로 선언했기 때문에 ! */
synchronized (this) {
    counter.count++;
}

/* 하나의 인스턴스를 생성하고, 이를 쓰레드의 인자로 넘겨주면 같은 Counter()라는 동기화 블록이 동기화 된다. */
Counter count = new Counter();
for (...) {
    thread[i] = new Thread(new MyRunnable(counter));
    thread[i].start();
}

지금까지 배운 mutex, semaphore, monitor상호배제만 해결한 동기화 도구이다.

그래서 그러한 문제도 해결해보자고 최근에 나온것이 바로 Liveness이다.

반응형