티스토리 뷰

OS

[OS] 프로세스 동기화 & 상호배제

cherishee 2020. 3. 18. 22:16

프로세스 동기화 & 상호배제

다중 프로그래밍 시스템은 여러 개의 프로세스들이 존재한다. 이 프로세스들은 서로 독립적으로 동시에 동작하는데 공유 자원 또는 데이터가 있을 때 문제가 발생할 수 있다. 즉, 같은 공간에 작업을 하면 문제가 발생!!

동기화(Process Synchronization)

  • 프로세스들이 서로 동작을 맞추는 것이다.
  • 프로세스들이 서로 정보를 공유하는 것이다.

비동기화

  • 프로세스들이 서로에 대해 모른다.

그렇다면 왜 동기화가 필요한가?

  • 병행 수행중인 비동기적 프로세스들이 공유자원에 동시에 접근할 때 문제가 발생 할 수 있다.
  • 프로세스가 공유 자원에 접근할 때, 경쟁 조건이 발생하면 공유 자원을 신뢰할 수 없게 만들 수 있다. 이를 방지하기 위해 프로세스들이 공유 자원을 사용할 때 특별한 규칙을 만드는 것이 프로세스 동기화이다.

용어 정리

  • Shared data(공유 데이터) or Critical data : 여러 프로세스들이 공유하는 데이터
  • Critical section(임계 영역) : 공유 데이터를 접근하는 코드 영역
  • Mutual exclusion(상호배제) : 둘 이상의 프로세스가 동시에 critical section에 진입하는 것을 막는 것
  • Race Condition (경쟁 조건) : 여러 프로세스가 공유 자원에 동시에 접근할 때 실행 순서에 따라 실행 결과가 달라질 수 있는 상황

Critical Section(임계 영역)

  • 멀티 프로세스 환경에서 둘 이상의 프로세스가 동시에 접근해서는 안되는 공유 자원의 코드 영역이다.
  • 즉 , 경쟁 조건이 발생 할 수 있는 프로그램 코드 부분이다.
  • 임계구역 문제를 해결하기 위한 3가지
    1. Mutual Exclusion(상호배제) : 하나의 프로세스가 임계 영역에 들어가 있으면 다른 프로세스는 들어갈 수 없게 막아주는 것이다.
    2. Progress(진행) : 임계 영역에 들어간 프로세스가 없다면, 어느 프로세스가 들어갈 것인지 적절히 선택해줘야 한다. 즉, 임계 영역안에 있는 프로세스 외에는, 다른 프로세스가 임계 영역에 진입하는 것을 방해하면 안된다.
    3. Bounded Waiting(한정 대기) : 기아상태를 방지하기 위해, 한 번 들어갔다 나온 프로세스는 다음에 들어갈 때 제한을 준다. (언젠간 진입 해야한다.)

Mutual Exclusion(상호배제) 구현

Race condition과 같은 문제를 막기 위해서 등장한 개념이 '상호배제'이다. 즉, 하나의 프로세스가 실행하는 동안 다른 프로세스가 들어와서는 안된다.

Mutual Exclusion primitives(가장 기본이 되는 연산)

  • enterCS (똑똑!! 누가 안에 있는지 검사하는 것)

    • 임계 영역 진입 전 검사하는 것이다.
    • 다른 프로세스가 임계 영역안에 있는지 검사한다.
  • exitCS (기다리는얘 들어와! 라고 알리는 것)

    • 임계 영역을 벗어날 때 후처리하는 과정이다.
    • 임계 영역을 벗어남을 시스템이 알린다.

Case 1.

서로 turn을 사용하여 번갈아가면서 사용하는 경우이다. 만약 P0가 임계 영역으로 들어가기 전에 죽어버리는 상황이 발생하면 turn은 여전히 0 이기 때문에 임계 영역이 비어있어도 P1의 turn(1)이 아니기 때문에 사용할 수 없다. => Progress(진행) 조건을 위배한다. P0가 임계 영역에 진입하지 않는 경우 한 프로세스가 두 번 연속 임계 영역에 진입이 불가하다.

Case 2.

Flag를 사용한다. 프로세스가 들어가면 깃발(flag)을 올리고 나오면 내린다. 만약 P0에서 flag를 변경하기 전에 preemption이 되었다. 이때 P1이 들어가서 일을 하게 되면, P0가 자신의 일을 마치고 돌아오면 임계 영역에 들어가게된다. 그렇게 되며 임계 영역에 두 개의 프로세스가 존재하게 된다. => 상호배제 조건을 위배한다.

Case 3.

Case 2번과 같은 원리로 flag를 사용한다. 하지만 flag를 먼저 바꾸고 들어간다. 만약 P0에서 깃발을 먼저 바꾸고 preemption이 발생하고, 이 때 P1이 접근하려할 때 깃발을 먼저 바꾸고 P0를 기다린다. 일을 마치고온 P0가 들어가려고 할 때 상대방의 깃발을 확인하는데 이때, 깃발이 들려있게 되면 P0도 P1을 기다리게 된다. => Bounded Waiting(한정 대기)와 Progress(진행) 조건을 위배한다.


Mutual Exclusion Solutions

  • SW solutions
    • Dekker's algorithm
    • Dijkstra's algorithm
  • HW solution
    • TestAndSet (TAS) instruction
  • OS supported SW solution
    • Spinlock
    • Semaphore
    • Eventcount/sequencer
  • Language-Level solution
    • Monitor

SW Solutions

Dekker's algorithm : 프로세스가 두 개일 때 상호배제를 보장하는 최초의 알고리즘

turn과 flag 두 가지를 사용하여 구현한 것이다. 먼저 깃발을 들고 확인한 다음 turn을 확인하는 방법이다. turn을 확인하고 자신의 차례가 아니면 깃발을 내리고 자신의 차례면 기다린다. 자기 차례가 오면 깃발을 들고 CS에 진입하고, 끝나면 깃발내리고 차례를 넘겨준다.

Dijkstra's algorithm : 프로세스 n개의 상호배제 문제를 해결한 알고리즘

Flag 값 의 미
idle 프로세스가 임계 구역 진입을 시도하고 있지 않을 때
Want-in 프로세스가 임계 구역 진입 시도 1단계일 때
In-CS 프로세스의 임계 구역 진입 시도 2단계 및 임계 구역 내에 있을 때

  • Step 1 : 현재 턴인 프로세스가 일을 끝날 때까지 기다렸다가 내 턴으로 뺏는다.

  • Step 2 : 여러개의 프로세스가 들어 갈 수 있다. 여기서 깃발을 in-cs(나 2단계야)로 바꾼다. while문 조건을 만족하게 된다면 결국 in-cs에 나 혼자 있을 때, 임계 영역에 들어간다. (누군가가 있으면 until에서 걸려서 다시 repeat문을 실행한다.) 이 경우, 여러번 돌 수 있지만, 한정 대기 조건을 위배하지 않는다.

SW Solutions 단점

  1. 속도가 느리다.
  2. 구현이 복잡하다.
  3. 상호배제 실행 중 preemption 될 수 있다.
  4. Busy waiting : 기다리는데.. 빙빙 돌면서 기다림(비효율적이다.)

HW Solutions

  • 구현이 간단하다.
  • Busy waiting 문제가 있다. (semaphore를 사용해야 함)

TestAndSet(TAS) instruction

  • Test 와 Set을 한 번에 수행하는 기계어 (TestAndSet(boolean *target)은 target을 true로 바꿔주는 메소드, 방해받지 않고 한 번에 수행된다.)

  • 실행 중 인터럽트를 받지 않아서 preemption 되지 않는다.

  • Target의 현재 값을 반환하면서 target 값을 바꾸는 것을 한 번에 수행하게 된다. 이렇게 하면 상호배제 문제가 발생하지않는다.


OS supported SW Solutions

Spinlock

  • 정수 변수

  • 초기화, P(), V() 연산으로만 접근 가능하다.

    • 위의 연산들은 OS가 보장해서, 전체가 한 instruction cycle 에 수행 된다.
  • 상호배제 문제 해결!! 하지만 CPU가 여러개인 환경(멀티 프로세서)에서만 사용이 가능하다.

  • Busy waiting 문제 발생할 수 있다.

Semaphore

특징

  • Dijkstra가 제안
  • Busy waiting 문제 해결 : 기다려야 하는 프로세스는 block 상태가 된다.
  • 세마포어는 음이 아닌 정수형 변수(S)이다.
  • 초기화, P(), V() 연산으로만 접근 가능하다.
    • P : Probern(검사) : 들어가기 전에 검사
    • V : Verhogen(증가) : 나올 때 물건 반납!!
  • 임의의 S 변수 하나에 ready queue (대기실) 하나가 할당 된다.
  • Semaphore queue에 대한 wake-up 순서는 비결정적이다. -> 기아현상 발생 할 수 있다.

종류

  1. Binary semaphore : 상호배제나 프로세스 동기화의 목적으로 사용, S가 0과 1 두 값만 갖는다.
  2. Counting semaphore : 생산자-소비자 문제를 해결하기 위해 사용, S가 0이상의 정수 값을 갖는다.

연산

  • 초기화 연산

    • S는 물건의 양으로 S가 있으면 그 값으로 CS에 들어간다.

    • P는 자물쇠를 거는 연산이다.

    • V는 자물쇠를 푸는 연산이다.

    • 만약 CS에 들어갈 수 없으면, 빙글빙글 도는 것이 아니라(while 문) ready queue에서 대기하게 된다.

    • P() 연산과 V() 연산


Semaphore(세마포어)로 해결 가능한 동기화 문제들

1. 상호배제 문제

spinlock은 물건이 없으면 뱅뱅 돌았음 (Busy waiting) 하지만 세마포어는 대기실(ready queue)에서 기다린다. (하고 있는 일을 다하면 와서 깨워준다.)

2. 프로세스 동기화 문제

프로세스들의 실행 순서를 맞춰준다. 프로세스들은 병행적이며, 비동기적으로 수행한다.

sync가 세마포어 변수이다. Pj가 sync라는 물건을 가지고 있다고 할 때, sync는 0 (즉, 물건이 없는 상태)이기 때문에, ready queue에서 대기한다. Pj가 일을 끝내고 오면 sync에 1을 반납하고, Pi를 wakeup한다.

3. 생산자-소비자 문제

  • 생산자 프로세스 : 메시지를 생성하는 프로세스 그룹
  • 소비자 프로세스 : 메세지를 전달받는 프로세스 그룹

Buffer가 n인 경우엔 원형 큐를 사용한다. mutex는 생산자, 소비자가 여러명일 수 있기 때문에 사용한다. 그래서 처음에 mutex로 묶고 들어간다.

  • 생산자 입장에서 보면, 공간이 있는지 확인을 하고 만약 없으면 ready queue에서 대기한다. 있으면 buffer에 물건을 넣고 index를 갱신하고 물건 수를 하나 늘린다.
  • 소비자 입장에서 보면, 물건이 있는지 확인하고 만약 없으면 ready queue에서 대기한다. 있으면 물건을 빼고 공간 수를 하나 늘린다.

4. Reader-writer 문제

  • Reader : 데이터에 대해 읽기 연산만 수행 (여러명 가능)

  • Writer : 데이터에 대해 갱신 연산을 수행(한명만 가능)

  • 데이터 무결성 보장 필요!!

    • Reader들은 동시에 데이터 접근 가능
    • Writer들이 동시에 데이터 접근 시, 상호배제 필요
  • 해결법!!

    • reader, writer 에 대한 우선권을 부여한다.

      위의 방법은 reader가 우선권을 가지는 방법이다.

5. Dining philosopher 문제

Code

Semaphore : Producer-Conumer problem

다음은 생산자 소비자 문제이다.  처음에 mutex 만 사용하였지만 busy waiting 문제가 발생하였다. Busy waiting은 생산과 소비를 하기 전에 버퍼가 가득 찼는지 비어 있는지 확인하는 무한 반복문을 말한다. 그래서 세마포어 변수 procon 을 추가하였다.

  • pro : 생산자를 위한 변수이다. 즉, 버퍼에서 비어있는 공간의 개수(초기값 size )이다.
  • con : 소비자를 위한 변수이다. 즉, 버퍼에서 차있는 공간의 개수(초기값 0)이다.

위의 세마포어 변수를 사용하여 데이터를 생성 및 가져오기 전에 공간 또는 데이터가 있는지 확인한다. 만약 있다면 mutex (임계 영역)으로 들어가서 작업을 수행한다.

import java.util.concurrent.Semaphore;

public class ProducerConsumerProblem {
    public static void main(String[] args) {
        Buffer buffer = new Buffer(100);
        Producer producer = new Producer(buffer, 10000);
        Consumer consumer = new Consumer(buffer, 10000);
        producer.start();
        consumer.start();
        try {
            producer.join();
            consumer.join();
        } catch (InterruptedException e) {
        }
    }

    static class Buffer {
        int size;
        int in;
        int out;
        int[] buffer;
        int count;
        Semaphore mutex, pro, con;

        Buffer(int size) {
            this.size = size;
            buffer = new int[this.size];
            in = out = count = 0;
            mutex = new Semaphore(1);
            pro = new Semaphore(size);  // 생산자를 위한 버퍼
            con = new Semaphore(0); // 소비자를 위한 버퍼
        }

        void putIn(int item) {
            while (count == size) ;
            try {
                pro.acquire(); // 버퍼의 비어있는 공간을 1 감소시킨다.(비어있는 공간이 없으면 block)
                mutex.acquire();
                count++;
                buffer[in] = item;
                System.out.println(buffer[in] + "을 생산했습니다.");
                in = (in + 1) % size;
                mutex.release();
                con.release(); // 버퍼에 찬 공간을 1 증가시킨다.
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        int takeOut() {
            while (count == 0) ;
            int output = 0;
            try {
                con.acquire(); // 버퍼의 찬 공간을 1 감소시킨다.(버퍼가 모두 비어있으면 block)
                mutex.acquire();
                count--;
                output = buffer[out];
                System.out.println(output + "을 소비했습니다.");
                out = (out + 1) % size;
                mutex.release();
                pro.release(); // 버퍼의 비어있는 공간을 1 증가 시킨다.
                return output;

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return -1;
        }
    }

    static class Producer extends Thread {
        Buffer b;
        int cnt;

        Producer(Buffer b, int cnt) {
            this.b = b;
            this.cnt = cnt;
        }

        public void run() {
            for (int i = 0; i < cnt; i++) {
                b.putIn(i);
            }
        }
    }

    static class Consumer extends Thread {
        Buffer b;
        int cnt;

        Consumer(Buffer b, int cnt) {
            this.b = b;
            this.cnt = cnt;
        }

        public void run() {
            for (int i = 0; i < cnt; i++) {
                b.takeOut();
            }
        }
    }
}

출처 : https://codemcd.github.io/study/OperatingSystem-9%EC%9E%A5-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8F%99%EA%B8%B0%ED%99%94-2/

Semaphore : Dining philosopher problem

식사하는 철학자는 대표적인 교착상태의 문제이다. 간략하게 설명하자면 철학자들은 식사를 하거나 생각을 하거나 두 가지 동작만 무한 반복한다. 식사를 할 때는 자신의 양 옆에 있는 젓가락을 사용한다. 한 철학자가 젓가락을 사용하면 다른 철학자는 사용할 수 없다. 즉, 한 젓가락에 동시에 접근하는 것은 불가능하다.

젓가락에 접근하는 순서는 왼쪽 젓가락을 들고 그 다음 오른쪽 젓가락을 가져간다.

image-20200318215918750

세마포어를 사용하면 생각보다 어렵지 않다. 아래와 같이 세마포어를 사용하고 permit을 1 로 제한한다. 하지만 여기서 교착상태 문제가 발생한다. 교착상태는 어떤자원을 갖고있을 때 다른 자원을 갖고자 할 때 발생하는 문제이다. 이 경우 모든 철학자가 왼쪽 젓가락을 들고 있을 때 , 오른쪽 젓가락에 아무도 접근할 수 없는 경우에 발생한다.

import java.util.concurrent.Semaphore;
public class DiningPhilosopherProblem {
    public static void main(String[] args) {
        Semaphore[] sticks = new Semaphore[5];
        Philosopher[] philosophers = new Philosopher[5];
        for (int i = 0; i < 5; i++) {
            sticks[i] = new Semaphore(1);
        }
        for (int i = 0; i < 5; i++) {
            philosophers[i] = new Philosopher(i, sticks[i], sticks[(i + 1) % 5]);
        }
        for (int i = 0; i < 5; i++) {
            philosophers[i].start();
        }
    }
    static class Philosopher extends Thread {
        int id;
        Semaphore lStick, rStick;

        Philosopher(int id, Semaphore lStick, Semaphore rStick) {
            this.id = id;
            this.lStick = lStick;
            this.rStick = rStick;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    lStick.acquire();
                    rStick.acquire();
                    eating();
                    lStick.release();
                    rStick.release();
                    thinking();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        void eating() {
            System.out.println(id + "가 먹고있는 중..");
        }

        void thinking() {
            System.out.println(id + "가 생각하고 있는 중..");
        }

    }
}

해결 방법은 아래와 같다. 철학자의 id 값이 짝수이면 그 철학자는 왼쪽부터 젓가락을 들고 홀수이면 오른쪽부터 젓가락을 들게 하여 교착상태가 일어나지 않게한다.

@Override
public void run() {
    try {
        while (true) {
            if(id % 2==0){
                lStick.acquire();
                rStick.acquire();
            }else{
                rStick.acquire();
                lStick.acquire();
            }
            eating();
            lStick.release();
            rStick.release();
            thinking();
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Eventcount / Sequencer

  • 세마포어의 문제점인 기아현상 해결한다.

  • 세마포어보다 더 low-level control 가능하다.

  • 은행의 번호표와 비슷한 개념이다.

  • Sequencer (번호표 뽑는 기계)

    • 정수형 변수이다.
    • 생성시 0으로 초기화, 감소하지 않는다.
    • 발생 사건들의 순서 유지한다.
    • ticket() 연산으로만 접근 가능하다.
  • ticket(S)

    • 현재까지 ticket() 연산이 호출 된 횟수를 반환한다.
  • Eventcount

    • 정수형 변수이다.
    • 생성시 0으로 초기화, 감소하지 않는다.
    • 특정 사건의 발생 횟수를 기록한다.
    • read(E), advance(E), await(E,V) 연산으로만 접근 가능하다.
  • read(E) : 현재 eventcount 값 반환한다.

  • advance(E) : E <- E+1 , E를 기다리고 있는 프로세스를 깨움 (은행원이 띵동~하는 것)

  • await(E,V) : V는 정수형 변수, if(E<V)이면 E에 연결된 큐에 프로세스 전달 및 cpu 스케줄러 호출

생산자-소비자 문제


세마포어 예제 코드

멀티 스레드 프로그래밍을 할 때, 한정된 리소스를 각 스레드에게 분배해야 하는 경우가 있다. 아래의 코드 처럼 쓰레드는 20개이지만 리소스 개수는 5개인 경우 5개만 먼저 사용하고 나머지는 대기하다가 리소스가 해제시 사용한다.

import java.util.Random;
import java.util.concurrent.Semaphore;

/**
 * Semaphore를 이용한 한정된 자원 사용
 */
public class SemaphoreEx {
    public static void main(String[] args) {
        System.out.println("Starting...");
        BoundedResource resource = new BoundedResource(5);
        //10개의 스레드가 생성되어있지만, 동시에 리소스를 사용할 수 있는 스레드는 5개임
        for(int i=0;i<20;i++){
            new UserThread(resource).start();
        }
    }

}

class UserThread extends Thread {
    private final static Random random = new Random(1000);
    private final BoundedResource resource;

    public UserThread(BoundedResource resource) {
        this.resource = resource;
    }
    public void run(){
        try {
            while(true){
                resource.use();
                Thread.sleep(random.nextInt(3000));
            }
        }catch(InterruptedException e){
            Log.show("InterruptedException : "+e);
        }
    }
}

class BoundedResource {
    private final Semaphore semaphore;
    private final int nPermits;
    private final static Random random = new Random(1000);

    public BoundedResource(int cnt) {
        this.semaphore = new Semaphore(cnt);
        this.nPermits = cnt;
    }

    public void use() throws InterruptedException {
        semaphore.acquire(); // 세마포어 리소스 확보
        try {
            doUse();
        } finally {
            semaphore.release(); //세마포어 리소스 해제
        }
    }

    protected void doUse() throws InterruptedException {
        /*
        nPermits - semaphore.availablePermits()
        = 최대 리소스 개수 - 세마포어에서 이용가능한 리소스 개수
        = 현재 사용중인 리소스 개수
         */
        Log.show("Begin : 현재 사용중인 리소스 개수 = " + (nPermits - semaphore.availablePermits()));
        Thread.sleep(random.nextInt(500));
        Log.show("End : 현재 사용중인 리소스 개수 = " + (nPermits - semaphore.availablePermits()));
    }

}

class Log {
    public static void show(String msg) {
        System.out.println(Thread.currentThread().getName() + ":" + msg);
    }
}

사실 여태까지 위에서 정리한 세마포어는 상호배제를 위한 방법으로 알고있었는데 막상 예제를 찾아보니 한정된 자원을 사용할 때 많이 쓰이는 것 같다. 그래서 조금 더 찾아본 결과 다음과 같다.

뮤텍스는 상호배제 알고리즘으로 synchronization로 만든 블록 사이의 로직이 실행이 다 끝날 때까지 락을 걸어 사용한다고 했을 때, 세마포어는 상호배제 알고리즘을 사용하나 거기에 임계영역에 대한 범위를 만들어서 자원을 보호한다고 생각하면 된다. 참고로 이진 세마포어에서 임계 영역이 0과 1을 갖는 쓰레드나 프로세스는 뮤텍스라고 생각해도 무방하다.

참고 사이트


JAVA Thread Synchronized, Semaphore 차이

  • 둘 다 없는 경우 : 다수의 스레드가 접근하여 동기화 문제 발생한다.
  • Synchronized 사용 : 하나의 스레드만 접근할 수 있게 한다.
  • Semaphore 사용 : 사용자가 직접 접근 가능한 스레드의 수를 지정한다.

1. Synchronized, Semaphore 둘 다 없는 경우

스레드 동기화에 대한 아무런 처리를 하지 않았기 때문에 수행결과는 순서를 보장하지 않고 무작위로 출력된다.

[Thread-0] : 시작
[Thread-6] : 시작
[Thread-7] : 시작
[Thread-4] : 시작
[Thread-8] : 시작
[Thread-9] : 시작
[Thread-5] : 시작
[Thread-3] : 시작
[Thread-1] : 시작
[Thread-2] : 시작
[Thread-8] : 종료
[Thread-0] : 종료
[Thread-6] : 종료
[Thread-3] : 종료

2. Synchronized 사용

Synchronized를 사용하면 하나의 스레드만 자원을 사용할 수 있게 된다. 따라서 아래와 같이 하나의 스레드가 시작하고 종료해야지 다른 스레드가 작업할 수 있어 순서를 보장한다. 즉, Synchronized는 어떤 스레드가 진입하는 순간 다른 스레드의 진입을 막는다.

[Thread-0] : 시작
[Thread-0] : 종료
[Thread-9] : 시작
[Thread-9] : 종료
[Thread-8] : 시작
[Thread-8] : 종료
[Thread-7] : 시작
[Thread-7] : 종료

3. Semaphore 사용

Synchronized는 하나의 스레드만 가능했다면 Semaphore는 동시에 실행 할 수 있는 스레드의 수를 제어할 수 있다.

[Thread-1]3쓰레드가 점유중
[Thread-2]4쓰레드가 점유중
[Thread-0]3쓰레드가 점유중
[Thread-4]4쓰레드가 점유중
[Thread-3]5쓰레드가 점유중
[Thread-5]5쓰레드가 점유중
[Thread-6]5쓰레드가 점유중

Code

import java.util.concurrent.Semaphore;

public class SemaphoreAndSynchronized {
    public static void main(String[] args) {
        final SomeResource resource = new SomeResource(5);
        for (int i = 1; i <= 10; i++) {
            Thread t = new Thread(new Runnable() {
                public void run() {
                    resource.useSynchronized();
                }
            });
            t.start();
        }

    }
}

class SomeResource {
    private final Semaphore semaphore;
    private final int maxThread;

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

    public void useSemaphore() {
        try {
            semaphore.acquire(); // Thread 가 semaphore에게 시작을 알림
            System.out.println("[" + Thread.currentThread().getName() + "]" + (maxThread - semaphore.availablePermits()) + "쓰레드가 점유중"); // semaphore.availablePermits() 사용가능한 Thread의 숫자
            Thread.sleep((long) (Math.random() * 10000));
            semaphore.release(); // Thread 가 semaphore에게 종료를 알림
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public synchronized void useSynchronized(){
        try {
            System.out.println("[" + Thread.currentThread().getName() + "] : 시작" ); // semaphore.availablePermits() 사용가능한 Thread의 숫자
            Thread.sleep((long) (Math.random() * 10000));
            System.out.println("[" + Thread.currentThread().getName() + "] : 종료" ); // semaphore.availablePermits() 사용가능한 Thread의 숫자

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

출처: https://javaplant.tistory.com/30 [자바공작소]

본 게시글은 공부를 목적으로 작성합니다.

강의자료 및 강의의 저작권은 다음과 같습니다.

https://sites.google.com/view/hpclab/courses/operating-system

[

HPC Lab., KOREATECH - Operating System

Copyright © High Performance, Heterogeneous Parallel Computing Lab, KOREATECH. All right reserved.

sites.google.com

](https://sites.google.com/view/hpclab/courses/operating-system)

문제시 삭제하겠습니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함