본문 바로가기
CS/OS

[운영체제] 7. 프로세스 동기화 도구 : 모니터 Monitor

by DenverAlmighty 2024. 12. 11.

KOCW에서 제공하는 경성대학교  양희재 교수 운영체제 강의를 듣고 정리한 글 입니다.



1. 모니터

1) 모니터란?

  • 세마포 이후 프로세스 동기화 도구이다. 세마포 보다 고수준 개념이다.
  • 세마포어보다는 구조가 복잡하지만, 사용하기는 더 편리하다.

 

2) 모니터 구조

모니터 구조

모니터는 공유 자원공유 자원 접근 메소드로 이루어져 있다.
모니터에는 배타 동기를 위한 큐조건 동기를 위한 큐 2개가 있다.

공유 자원 접근 메소드는 최대 1개 메소드만 접근할 수 있다. 
나머지 메소드는 배타 동기를 위한 큐에서 기다리고있는다.
진입했던 쓰레드는 wait() 콜에 의해 조건 동기를 위한 큐로 들어가게되고(block), 새로운 쓰레드는 진입 가능하다.
새 쓰레드는 notify()로 조건 동기 큐에 block되었던 쓰레드를 깨울 수 있다
깨워진 쓰레드는 현재 쓰레드가 나가면 재진입할 수 있다.

 

 

2. 자바 모니터

자바의 모든 객체는 모니터가 될 수 있다.

    • 배타동기: synchronized 키워드 사용하여 지정
    • 조건동기: wait(), notify(), notifyAll() 메소드 사용

모니터 사용 예시 코드

 

하나의 쓰레드만 공유자원 접근함수에 접근할 수 있다.
즉, 한 쓰레드가 f() 메소드를 쓰고있다면, 다른 쓰레드는 f() 또는 g()에 접근할 수 없다.

 

1) 모니터 사용

(1) 사용 방법

synchronized {
    /* Critical-Section */
}

모니터를 이용해 한번에 하나의 쓰레드만 공유자원을 이용하게 만들 수 있다. 단지 synchronized를 메소드 앞에 붙이면 된다.
synchronized 메소드를 사용하여 공통 변수를 업데이트 하는 메소드를 모니터라 한다.

 

(2) 사용 예시 1 : 상호 배제 Mutual Exclusion

은행 계좌 문제

/* Monitor : Mutual Exclusion
Bank Account Problem */

class BankAccount {
    int balance;
    synchronized void deposit(int amt) {
        int temp = balance + amt;
        System.out.print("+");
        balance = temp;
    }
    synchronized void withdraw(int amt) {
        int temp = balance - amt;
        System.out.print("-");
        balance = temp;
    }
    int getBalance() {
        return balance;
    }
}

/*
Output
+++++++++++++++++++++++++++--------------------------------------++++++++++------------------+++++++++++++++++++++++-----------+++++++++++++++++++++++++-------------------------+++++++++++++++--------
balance = 0
*/

잔액 0으로 정상적으로 출력된다.

 

(2) 사용 예시 2 :  순서 정하기 Ordering

모니터를 이용해 어느 메소드를 먼저 실행할지 설정할 수 있다.

P1 P2
  wait();
S1; S2;
notify();  

 

은행 계좌 문제에서는 아래처럼 하면 된다.

입금 먼저

위의 표에서 S1은  deposit, S2는 withdraw가 된다.

/* Monitor : Ordering
Bank Account Problem */

class BankAccount {
    int balance;
    synchronized void deposit(int amt) {
        int temp = balance + amt;
        System.out.print("+");
        balance = temp;
        notify();
    }
    synchronized void withdraw(int amt) {
        while (balance <= 0)
    	    try {
	            wait();
	        } catch (InterruptedException e) {}
        int temp = balance - amt;
        System.out.print("-");
        balance = temp;
    }
    int getBalance() {
        return balance;
    }
}


/*
Output
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------+++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------------------------------------
Balance = 0
*/

 

출금 먼저

notify(), wait()을 deposit, withdraw에 바꿔주면되는데 부등호 바꿔줘야한다.

 

입출금 번갈아가면서

/* Monitor : Ordering
Bank Account Problem */

class BankAccount {
    int balance;
    /* p_turn : Parent Turn */
    boolean p_turn = true;
    synchronized void deposit(int amt) {
        int temp = balance + amt;
        System.out.print("+");
        balance = temp;
        /* child 깨우고 자신 block */
        notify();
        p_turn = false;
        try {
            wait();
        } catch (InterruptedException e) {}

    }
    synchronized void withdraw(int amt) {
    	/* 처음에는 wait() */
        while (p_turn)
            try {
                wait();
            } catch (InterruptedException e) {}
        int temp = balance - amt;
        System.out.print("-");
        balance = temp;
        /* child 종료 후 notify()로 부모 깨우고, p_turn 바꾸기 */
        notify();
        p_turn = true;
    }
    int getBalance() {
        return balance;
    }
}


/*
Output
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/

최초에 p_turn이 true여서 입금 먼저 실행하게 된다.

 

 

(3) 사용 예시 3 :  생산자-소비자 문제 Bounded Buffer Problem 

 

/* Monitor : Bounded Buffer Problem  */

class Buffer {
    int[] buf;
    int size;
    int count;
    int in ;
    int out;

    Buffer(int size) {
        buf = new int[size];
        this.size = size;
        count = in = out = 0;
    }

    synchronized void insert(int item) {
        while (count == size)
            try {
                wait();
            } catch (InterruptedException e) {}
        buf[ in ] = item; in = ( in +1) % size;
        notify();
        count++;
    }

    synchronized int remove() {
        while (count == 0)
            try {
                wait();
            } catch (InterruptedException e) {}
        int item = buf[out];
        out = (out + 1) % size;
        count--;
        notify();
        return item;
    }
}



/*
Output
Number of items in the buf is 0
*/

 

(4) 사용 예시 : 식사하는 철학자 문제  Dining Philosopher Problem 

Semaphore 대신 모니터를 사용해 Chopstick 으로 대체하였다.

/* Monitor : Dining Philosopher Problem */

class Philosopher extends Thread {
    int id; // philosopher id
    Chopstick lstick, rstick; // left, right chopsticks
    Philosopher(int id, Chopstick lstick, Chopstick rstick) {
        this.id = id;
        this.lstick = lstick;
        this.rstick = rstick;
    }
    public void run() {
        try {
            while (true) {
                lstick.acquire();
                rstick.acquire();
                eating();
                lstick.release();
                rstick.release();
                thinking();
            }
        } catch (InterruptedException e) {}
    }

    void eating() {
        System.out.println("[" + id + "] eating");
    }
    void thinking() {
        System.out.println("[" + id + "] thinking");
    }
}


class Chopstick {
    private boolean inUse = false;
    synchronized void acquire() throws InterruptedException {
        while (inUse)
            wait();
        inUse = true;
    }
    synchronized void release() {
        inUse = false;
        notify();
    }
}

class Main {
    static final int num = 5; // number of philosphers & chopsticks
    public static void main(String[] args) {
        int i;
        /* chopsticks */
        Chopstick[] stick = new Chopstick[num];
        for (i = 0; i < num; i++)
            stick[i] = new Chopstick();
        /* philosophers */
        Philosopher[] phil = new Philosopher[num];
        for (i = 0; i < num; i++)
            phil[i] = new Philosopher(i, stick[i], stick[(i + 1) % num]);
        /* let philosophers eat and think */
        for (i = 0; i < num; i++)
            phil[i].start();
    }
}