본문 바로가기

CS/운영체제(OS)

OS - 모니터 (멀티 스레드 프로그래밍)

728x90

프로세스/쓰레드 동기화 도구 중 하나이다. 세마포어는 너무 오래된 것이고 최근 자바는 모니터를 지원한다.

 

모니터의 동작원리

 

  • Java의 모든 객체는 모니터가 될 수 있다.
    • 베타 동기 - Common Variable 은 오직 하나만 접근할 수 있다. → synchronized 키워드 사용
    • 조건 동기 - wait(), notify, notifyAll 메소드 사용
      • notifyAll 하면 모든 스레드가 풀려난다.

모니터는 사실 멀티코어 컴퓨팅 강의에서 배운 멀티 스레드 프로그래밍과 같은 것을 알게 되었다. 아래는 멀티코어 프로그래밍에서 과제로 해결한 소수찾기 문제이다.

package Task;

public class pc_dynamic {
    // 20만까지 테스트하기 위함
    private static final int NUM_END = 10;
    private static final int NUM_THREAD = 2;

    // 메인함수
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Task.pc_dynamic");
        Dynamic_Thread.counter = 0;
        // for 문에서 소수를 테스트하기 위한 변수
        int i;

        // 쓰레드 생성에 따른 시간 차이를 알기 위해 입력한 시간
        //long startTimeReal = System.currentTimeMillis();

        // 쓰레드 생성
        Dynamic_Thread[] thread = new Dynamic_Thread[NUM_THREAD];
        // 쓰레드에 시작값 할당
        // isPrime 는  처음 할당된 값들 이후부터 계산할 것이다.
        for (int t = 0; t < NUM_THREAD; t++) {
            // 배열은 0부터 시작이라 0에는 1을, 1에는 3을 할당하기 위해 1 추가.
            thread[t] = new Dynamic_Thread();
        }
        // 판단을 시작할 변수를 할당해줬다.
        Dynamic_Thread.isPrime = 1;

        //시작 시간을 측정하기 위한 변수
        long startTime = System.currentTimeMillis();
        // 소수인지 테스트
        // 쓰레드 시작
        for (int t = 0; t < NUM_THREAD; t++) {
            thread[t].start();
        }
        // 시간 측정을 위해 join 을 이용해 쓰레드가 끝날 때까지 대기
        for (int t = 0; t < NUM_THREAD; t++) {
            thread[t].join();
        }

        // 끝나는 시간을 측정하기 위한 변수
        long endTime = System.currentTimeMillis();
        long timeDiff = endTime - startTime;

        for (int t = 0; t < NUM_THREAD; t++) {
            System.out.println("Thread #" + t + " Execution Time : " + thread[t].timeDiff + "ms");
        }

        System.out.println("Execution Time : " + timeDiff + "ms");
        System.out.println("1..." + (NUM_END - 1) + " prime# counter=" + Dynamic_Thread.counter + "\\n");

    }

    // 소수 판별 함수. 판별하기 위한 숫자를 x로 받는다.
    private static boolean isPrime(int x){
        int i;
        if(x <= 1) return false;
        for(i = 2; i < x; i++){
            if(x % i == 0) return false;
        }
        return true;
    }

    static class Dynamic_Thread extends Thread {
        // 쓰레드에서 처음 판단할 숫자를 변수 x로 주었다.
        //  isPrime 에서 x를 할당 받아올 것이다.
        int x;
        // 소수 숫자를 세기 위해 counter 변수를 클래스 내에 선언해줬다.
        static int counter;
        // 소수인지 확인할 변수를 변수를 클래스 내에 선언해줬다.
        static int isPrime;
        // 쓰레드 끝난 후 더하기 위해 임시 변수 선언
        int temp = 0;
        // 쓰레드 시작 시간
        long startTime;
        // 쓰레드 종료 시간
        long endTime;
        // 쓰레드 실행 시간
        long timeDiff;

        public Dynamic_Thread() {
            this.x = 1;
        }

        public void run() {
            startTime = System.currentTimeMillis();
            while (isPrime < NUM_END) {
                for (int i = 0; i < 10; i++) {
                    if (this.x < NUM_END) {
                        if (isPrime(this.x)) temp++;
                        System.out.println(x);
                        this.x = update();
                    }
                }
            }

            // 클래스 내 counter 변수에 소수를 더한다.
            counter += temp;

            // 쓰레드 종료 시간
            endTime = System.currentTimeMillis();
            // 쓰레드 실행 시간
            timeDiff = endTime - startTime;
        }

        //여기를 synchronized 로 해줬다. x 를 isPrime 에서 가져오고 isPrime 는 2 더해준다.
        static synchronized int update() {
            isPrime = isPrime + 1;
            return isPrime;
        }
    }
}

위 코드는 1부터 20만까지 소수인지 판단하는 코드이다. 그러나 멀티쓰레드를 사용하였다. 그리고 로드밸런싱을 다이나믹으로 하였기 때문에 각자 스레드는 자신의 작업이 끝나면 바로 다음 숫자를 가져가려할 것이다.

그럼 여기서 만약 동기화 처리를 해주지 않으면 다음과 같은 문제가 생긴다.

1번 쓰레드 : 1

2번 쓰레드 : 2

3번 쓰레드 : 3

4번 쓰레드 : 4

위의 숫자들을 소수인지 아닌지 판단 한 후 이제 다음인 5를 판단하려고 1,2,3,4 모두가 달려들 것이다. 즉 위의 업데이트 함수에 한번에 모두 달려들었다. 이러면 안된다. 여기서 필요한 것이 synchronized 키워드이다.

 static synchronized int update() {
            isPrime = isPrime + 1;
            return isPrime;
        }

이렇게 하면 베타 동기로 하나밖에 접근하지 못하므로 올바른 값을 내보낼 수 있다.

 

 

이런 키워드를 배우면서도 이게 모니터라는 건지 몰랐는데 이번 기회에 알게 되었다. 아마도 배웠을 탠데 기억이 안난다 ㅎㅎ..

'CS > 운영체제(OS)' 카테고리의 다른 글

OS - 메모리관리: 페이징  (0) 2024.05.16
OS - 주기억장치 개요  (1) 2024.01.01
DeadLock (교착상태)  (0) 2023.12.11
OS - 식사하는 철학자 문제와 교착상태(DeadLock)  (0) 2023.11.27
전통적인 동기화 문제  (0) 2023.11.22