Java - 6. Thread: wait(), notify()

갓김치·2020년 9월 24일
0

고급자바

목록 보기
20/47

wait(), notify()

  • wait()
    • 동기화 영역에서 락을 풀고 Wait-Set영역 (공유객체별 존재)으로 이동시킨다.
  • notify() 또는 notifyAll()
    • Wait-Set 영역에 있는 쓰레드를 깨워서 실행할 수 있도록 한다.
    • nofity()는 하나, notifyAll()은 Wait-Set에 있는 전부를 깨운다
    • notify(): 랜덤이라 내가 원하는 애 말고 다른 애들을 깨울 수도 있음, 그럴바엔 그냥 notifyAll로 한번에 다 깨우는게 나음
  • wait, notify, notifyAll 모두 동기화 영역에서만 실행 가능
  • Object 클래스에서 제공하는 메서드이다

예시

  • 내가 스레드 1번, 공유객체 문열고 들어가, 작업다끝나고나면 락 풀어놔 ,사람들 들어올수있게
  • 2번이 거기 들어갔어, 잠궈놓고 일보고 다시 나와서 다시 풀어놓고 나가고
  • wait-set 영역
    • 대기실 같은 곳, 내가 지금 동기화영역에서 할 일이없어가지고 일시정지할라고 대기실 ㄱㄱ
    • 대기실은 공유객체에 대한 대기실, 각 공유객체마다 대기실이 따로따로 있음
    • 대기실에서 기다리는동안은 (=공유객체에서 나올때) 락풀고나와야함 다른사람들 들어갈수있게
    • notify안받으면 영원히 대기실에......
    • 딴사람이 작업하다가 할일 다하면 나 notify로 꺠우고 나도 일어나서 일하다가 다시 wait하면서 wait-set에서 기다리고 또 notify 하고 일하고

T19

1단계) 공유객체 클래스

class WorkObject {
  public synchronized void MethodA() {
    System.out.println("methodA에서 작업 중...");

    notify(); // Wait-Set영역에 있는 애들 깨우기 (깨울 애 없으면 그냥 뭐..)

    try {
      wait();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  
  public synchronized void methodB() {
    System.out.println("methodB에서 작업 중...");

    notify();

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

2단계) WorkObject의 methodA()메서드만 호출하는 쓰레드 클래스

class ThreadA extends Thread {
  private WorkObject workObj;
  
  public ThreadA(WorkObject workObj) {
    this.workObj = workObj;
  }
  
  @Override
  public void run() {
    for (int i = 0; i < 10; i++) {
      workObj.methodA();
    }
    System.out.println("ThreadA 종료");
  }
}

3단계) WorkObject의 methodB()메서드만 호출하는 쓰레드 클래스

class ThreadB extends Thread {
  private WorkObject workObj;
  
  public ThreadB(WorkObject workObj) {
    this.workObj = workObj;
  }
  
  @Override
  public void run() {
    for (int i = 0; i < 10; i++) {
      workObj.methodB();
    }
    System.out.println("ThreadB 종료");
  }
}

4단계) 메인에서 실행

public static void main(String[] args) {
  WorkObject workObj = new WorkObject();

  ThreadA tha = new ThreadA(workObj);
  ThreadB thb = new ThreadB(workObj);

  tha.start();
  thb.start();
}

결과

  • B를 깨워줄 A가 종료되어서 B는 영원히 잠들게되었따.......
  • 프로그램을 종료할 방법이 없음

T20

1단계) 공유 객체 클래스

class DataBox {
private String data;

  // 1. data값을 반환하는 메서드
  public synchronized String getData() {
    if(data == null) {
      try {
        wait(); // 데이터가 없을 땐 반환할 데이터가 없으니 wait으로 기다림
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    
    // data가 null이 아니면 data를 returnData에 담아서 반환함
    String returnData = data;
    System.out.println("읽어온 데이터 : " + returnData);
    
    // 데이터를 누군가에게 줬으니 내가 가진 데이터는 없음
    // => null로 셋팅
    // => 다시 getter가 호출되면 data==null 이어서 wait()
    data = null;
    System.out.println(Thread.currentThread().getName() + "notify()호출");
    notify();
    return returnData;
  }
  
  // 2. data가 null일 경우에만 자료를 셋팅하는 메서드
  public synchronized void setData(String data) {
    if(this.data != null) { // 값을 set하러 왔는데 이미 값이 있으니 할 일이 없으므로 wait
      try {
        wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    
    // data == null 이므로 파라미터로 받은 값으로 데이터 셋팅
    this.data = data;
    System.out.println("셋팅한 데이터 : " + this.data);
    
    // 혹시나 get하러 갔다가 값이 없어서 wait하고 있는 스레드를 위해 값 가져가라고 notify로 깨워준다
    System.out.println(Thread.currentThread().getName() + "notify()호출");
    notify();
  }
}

2단계) 데이터를 셋팅만 하는 쓰레드

class ProducerThread extends Thread {
  private DataBox dataBox;
  
  public ProducerThread(DataBox dataBox) {
    super("ProducerThread"); // 쓰레드 이름
    this.dataBox = dataBox;
  }

  @Override
  public void run() {
    for(int i=1; i <= 10; i++) {
      String data = "Data-" + i;
      System.out.println("dataBox.setData("+data+")호출");
      dataBox.setData(data);
    }
  }
}

3단계) 데이터를 읽어만 오는 쓰레드

class ConsumerThread extends Thread {
  private DataBox dataBox;

  public ConsumerThread(DataBox dataBox) {
    super("ConsumerThread");
    this.dataBox = dataBox;
  }

  @Override
  public void run() {
    for (int i = 1; i <= 10; i++) {
      String data = dataBox.getData();
      System.out.println("dataBox.getData() : " + data);
    }
    }
  }

4단계) main에서 실행

public static void main(String[] args) {
  DataBox dataBox = new DataBox();

  ProducerThread pth = new ProducerThread(dataBox);
  ConsumerThread cth = new ConsumerThread(dataBox);

  pth.start();
  cth.start();
}
profile
갈 길이 멀다

0개의 댓글