코드 그라데이션

Day31. Thread(쓰레드) (4) 쓰레드 사이의 통신 본문

Java/Mega

Day31. Thread(쓰레드) (4) 쓰레드 사이의 통신

완벽한 장면 2023. 4. 26. 13:47

쓰레드 사이의 통신

- 선행 쓰레드와 후행 쓰레드 구분!

(먼저 작업을 수행해야하는 쓰레드와 그걸 이어받아서 하는 쓰레드로 구분하여 만들 수 있다.)

 

구조

 

 

주의사항

※ notify(), notifyAll() 메서드는

Synchronized 메서드 내에서 사용 될 수 있다.


예제코드

package mega.backend_onemore.Day31;

class Factory{
  private int value;
  private boolean check = false; // 처음엔 false로 시작

  synchronized void send(int value) {
    while(check == true) { // false부터 시작하므로 처음에 얘는 안 돈다.
      try { // true여서 여기로 들어올 수 있고
        wait(); // 대기하게 됨.
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    this.value = value;
    System.out.println("만드는 사람 : 만든다"+this.value);
    notify(); // 얘가 여기서 깨웠지만 아직 true로 바뀐 게 아니기 때문에(얘가 깨우는순간 wait는 깨진다)->
    check = true; // 아래로 와서 이 작업들을 다 하고 true로 바꾸니까,
  }
  synchronized int get() {
    while(check == false) {
      try { // 원래 처음이 false였으니까 사는 사람은 여기에 들어와 있었을 것.
        wait(); // 얘가 기다리고 있었는데, / -> 여기서 돌고 있다. // 위에서 true로 바뀌면 얘도 나와서
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    System.out.println("사는 사람 : 산다"+this.value);
    notify(); // 자고있는 만드는 메서드를 깨워주고
    check = false; // 얘는 다시 false로 바꿔서 들어가서 또 잔다.
    return this.value;
    // 요러한 작업들을 계속 반복...
  }
}

class P extends Thread{
  Factory f;
  P(Factory f){
    this.f = f;
  }

  public void run() {
    for(int i = 0;i<10;i++) {
      f.send(i); // 얘는 send에 접근하고,
    }
  }
}

class C extends Thread{
  Factory f;
  C(Factory f){
    this.f = f;
  }

  public void run() {
    int temp = 0;
    for(int i = 0;i<10;i++) {
      temp = f.get(); // 얘는 get에 접근한다.
    }
  }
}


public class ThreadEX1 {

  public static void main(String[] args) {

    Factory f = new Factory();
    P p = new P(f);
    C c = new C(f);

    p.start();
    c.start();
  }

}

 

지금 이것만 봤을 때,

class Factory{
  private int value;
  private boolean check = false; // 처음엔 false로 시작

  synchronized void send(int value) {
    while(check == true) { // false부터 시작하므로 처음에 얘는 안 돈다.
      try { // true여서 여기로 들어올 수 있고
        wait(); // 대기하게 됨.
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    this.value = value;
    System.out.println("만드는 사람 : 만든다"+this.value);
    notify(); // 얘가 여기서 깨웠지만 아직 true로 바뀐 게 아니기 때문에(얘가 깨우는순간 wait는 깨진다)->
    check = true; // 아래로 와서 이 작업들을 다 하고 true로 바꾸니까,
  }
  synchronized int get() {
    while(check == false) {
      try { // 원래 처음이 false였으니까 사는 사람은 여기에 들어와 있었을 것.
        wait(); // 얘가 기다리고 있었는데, / -> 여기서 돌고 있다. // 위에서 true로 바뀌면 얘도 나와서
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    System.out.println("사는 사람 : 산다"+this.value);
    notify(); // 자고있는 만드는 메서드를 깨워주고
    check = false; // 얘는 다시 false로 바꿔서 들어가서 또 잔다.
    return this.value;
    // 요러한 작업들을 계속 반복...
  }
}

 

1. boolean의 최초 초기화 값은 false

2. 따라서 처음엔 get으로 접근하고, 얘는 wait에 걸려서 자고 있다.

3. 자고 있으면 만드는 사람에서 value 값을 가지고 작업을 수행하고 나서

4. notify()로 자고 있는 get을 깨워주고,

5. 본인은 true로 바꾸고 try문 안으로 들어가서 wait에 걸려서 잠든다.

6. 그럼 바뀐 get이 잠에서 깨어나서 나와서 산다를 출력해주고,

    자고 있는 만드는 메서드를 깨우고, 자기는 다시 false로 바꾼다.

7. 그리고 다시 false니까 들어가서 wait...

 

이러한 작업들을 계속 반복하고 있음.

 


다시 정리해보면

이러한 과정 반복.

처음 상황에는 false이기 때문에 send 아래
this.value = value;
System.out.println("만드는 사람 : 만든다"+this.value);
notify();
check = true;
이 부분은 동작을 할 거예요.
while문 속에는 못 들어가고.

이 때 get은 false이므로 while문 타고 들어가서 갇힌다.
(wait이 작동)
그리고 send에서 notify() 를 하고 true 로 바꿔주는 순간
일어나고, while문 바깥으로 나오죠.

나오면
System.out.println("사는 사람 : 산다"+this.value);
이거 출력하고
자기도 할 일을 해야 하니까,
다시 notify 후 true로 바꿔주면 send에서 while문에 접근할 수 있게 되므로
들어가서 잠든다(wait)

뭐 요런식으로 계속 왔다갔다 왔다갔다.

그런데 notify() 만 실행되었을 때는
아직 boolean 값을 바꿔준 건 아니기 때문에
일어나서 계속 반복문 안을 돌고 있는 상황이 초래된다!!!

=> 그래서 만든사람-> 사는사람 -> 만든사람 -> 사는사람 

요러한 순서로 반복되어 출력된다.

 


위 예제코드의 실행 매커니즘

 

코드로 간단히 보자면

 

이러한 형태!

728x90
Comments