[java] Java에서 wait () 및 notify ()를 사용하는 간단한 시나리오

완벽하게 간단한 시나리오, 예를 들어 Queue와 함께 사용하는 방법을 제안하는 자습서를 얻을 수 있습니까?



답변

wait()notify()방법은 특정 조건이 만족 될 때까지 차단하는 실을 수 있도록하는 메커니즘을 제공하도록 설계된다. 이를 위해 고정 크기의 백업 저장소 요소가있는 블로킹 큐 구현을 작성하려고한다고 가정합니다.

가장 먼저해야 할 일은 메소드가 기다리는 조건을 식별하는 것입니다. 이 경우 put()상점에 여유 공간이 확보 take()될 때까지 메소드를 차단하고 리턴 할 요소가있을 때까지 메소드를 차단 하려고합니다 .

public class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public synchronized void put(T element) throws InterruptedException {
        while(queue.size() == capacity) {
            wait();
        }

        queue.add(element);
        notify(); // notifyAll() for multiple producer/consumer threads
    }

    public synchronized T take() throws InterruptedException {
        while(queue.isEmpty()) {
            wait();
        }

        T item = queue.remove();
        notify(); // notifyAll() for multiple producer/consumer threads
        return item;
    }
}

대기 및 알림 메커니즘을 사용해야하는 방법에 대해 몇 가지 참고할 사항이 있습니다.

첫째, 당신은 보장하기 위해 필요로하는 모든 호출에 wait()notify()코드의 동기화 된 지역 내합니다 (함께있는 wait()notify()같은 객체에 동기화되는 호출). 표준 스레드 안전 문제 이외의 이유는 누락 된 신호로 인한 것입니다.

예를 들어 put()대기열이 가득 차면 스레드가 호출 되어 조건을 확인하고 대기열이 가득 찼음을 확인하지만 다른 스레드가 예약되어 있기 전에 차단할 수 있습니다. 그런 다음이 두 번째 스레드 take()는 큐의 요소이며 대기 스레드에 큐가 더 이상 채워지지 않았 음을 알립니다. 그러나 첫 번째 스레드는 이미 조건을 확인했기 때문에 wait()진행이 가능하더라도 일정이 변경 되면 간단히 호출 됩니다.

공유 객체를 동기화 take()하면 첫 번째 스레드가 실제로 차단 될 때까지 두 번째 스레드의 호출이 진행될 수 없으므로이 문제가 발생하지 않도록 할 수 있습니다.

둘째, 가짜 웨이크 업으로 알려진 문제로 인해 if 문이 아닌 while 루프에서 점검중인 조건을 넣어야합니다. 대기 스레드를 notify()호출 하지 않고 재 활성화 할 수있는 경우가 있습니다 . 이 검사를 while 루프에 넣으면 가짜 웨이크 업이 발생하면 조건이 다시 검사되고 스레드가 wait()다시 호출 됩니다.


다른 답변 중 일부에서 언급했듯이 Java 1.5 java.util.concurrent는 대기 / 알림 메커니즘에 대해 더 높은 수준의 추상화를 제공하도록 설계된 새로운 동시성 라이브러리 ( 패키지)를 도입했습니다 . 이러한 새로운 기능을 사용하면 다음과 같이 원래 예제를 다시 작성할 수 있습니다.

public class BlockingQueue<T> {

    private Queue<T> queue = new LinkedList<T>();
    private int capacity;
    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public void put(T element) throws InterruptedException {
        lock.lock();
        try {
            while(queue.size() == capacity) {
                notFull.await();
            }

            queue.add(element);
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public T take() throws InterruptedException {
        lock.lock();
        try {
            while(queue.isEmpty()) {
                notEmpty.await();
            }

            T item = queue.remove();
            notFull.signal();
            return item;
        } finally {
            lock.unlock();
        }
    }
}

물론 실제로 차단 대기열이 필요한 경우 BlockingQueue 인터페이스 의 구현을 사용해야합니다 .

또한 이와 같은 것들 에 대해서는 동시성 관련 문제 및 솔루션에 대해 알아야 할 모든 것을 다루기 때문에 Java Concurrency in Practice를 강력히 권장 합니다.


답변

큐 예제는 아니지만 매우 간단합니다 🙂

class MyHouse {
    private boolean pizzaArrived = false;

    public void eatPizza(){
        synchronized(this){
            while(!pizzaArrived){
                wait();
            }
        }
        System.out.println("yumyum..");
    }

    public void pizzaGuy(){
        synchronized(this){
             this.pizzaArrived = true;
             notifyAll();
        }
    }
}

몇 가지 중요한 사항 :
1) 절대로하지 마십시오

 if(!pizzaArrived){
     wait();
 }

항상 while (조건)을 사용하십시오.

  • a) 스레드는 아무에게도 알리지 않고 대기 상태에서 산발적으로 깨어날 수 있습니다. (피자 남자가 차임을 울리지 않더라도 누군가 피자를 먹어보기로 결정했을 것입니다.)
  • b) 동기화 잠금을 획득 한 후 상태를 다시 확인해야합니다. 피자가 영원히 지속되지 않는다고 가정 해 봅시다. 피자에 줄을 서서 깨우지 만 모든 사람에게 충분하지 않습니다. 확인하지 않으면 종이를 먹을 수 있습니다! 🙂 (아마 좋은 예가 될 것
    while(!pizzaExists){ wait(); }입니다.

2) 대기 / 없음을 호출하기 전에 잠금 (동기화)을 유지해야합니다. 스레드는 깨어나 기 전에 잠금을 획득해야합니다.

3) 동기화 된 블록 내에서 잠금을 얻지 말고 외계인 메서드 (어떻게하는지 잘 모르는 방법)를 호출하지 않도록 노력하십시오. 필요한 경우 교착 상태를 피하기위한 조치를 취하십시오.

4) notify ()에주의하십시오. 무엇을하고 있는지 알 때까지 notifyAll ()을 고수하십시오.

5) 마지막으로 Java Concurrency를 실제로 읽으십시오 !


답변

비록 당신을 요구 wait()하고 notify()특히,이 견적 여전히만큼 중요하다고 생각합니다 :

Josh Bloch, Effective Java 2nd Edition , 항목 69 : 동시성 유틸리티를 선호 wait하고 notify(강조) :

사용의 어려움을 감안 wait하고 notify정확하게, 대신 더 높은 수준의 동시성 유틸리티를 사용한다 […]를 사용 wait하고 notify이 제공하는 높은 수준의 언어에 비해 직접하는 것은, “동시성 어셈블리 언어”로 프로그래밍 같다 java.util.concurrent. 이유 사용,면 적, 거의 없습니다 waitnotify새로운 코드 .


답변

Java Tutorial을 보셨습니까 ?

또한 실제 소프트웨어에서 이런 종류의 물건을 가지고 노는 것을 피하는 것이 좋습니다. 그것을 가지고 노는 것이 좋으므로 그것이 무엇인지 알 수 있지만 동시성은 모든 곳에서 함정을 가지고 있습니다. 다른 사람을위한 소프트웨어를 구축하는 경우 더 높은 수준의 추상화와 동기화 된 컬렉션 또는 JMS 대기열을 사용하는 것이 좋습니다.

그것은 적어도 내가하는 일입니다. 나는 동시성 전문가가 아니므로 가능한 한 손으로 스레드를 처리하는 것을 멀리합니다.


답변

public class myThread extends Thread{
     @override
     public void run(){
        while(true){
           threadCondWait();// Circle waiting...
           //bla bla bla bla
        }
     }
     public synchronized void threadCondWait(){
        while(myCondition){
           wait();//Comminucate with notify()
        }
     }

}
public class myAnotherThread extends Thread{
     @override
     public void run(){
        //Bla Bla bla
        notify();//Trigger wait() Next Step
     }

}


답변

스레딩의 wait () 및 notifyall ()의 예

동기화 된 정적 배열 목록은 리소스로 사용되며 배열 목록이 비어 있으면 wait () 메서드가 호출됩니다. notify () 메소드는 배열 목록에 요소가 추가되면 호출됩니다.

public class PrinterResource extends Thread{

//resource
public static List<String> arrayList = new ArrayList<String>();

public void addElement(String a){
    //System.out.println("Add element method "+this.getName());
    synchronized (arrayList) {
        arrayList.add(a);
        arrayList.notifyAll();
    }
}

public void removeElement(){
    //System.out.println("Remove element method  "+this.getName());
    synchronized (arrayList) {
        if(arrayList.size() == 0){
            try {
                arrayList.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }else{
            arrayList.remove(0);
        }
    }
}

public void run(){
    System.out.println("Thread name -- "+this.getName());
    if(!this.getName().equalsIgnoreCase("p4")){
        this.removeElement();
    }
    this.addElement("threads");

}

public static void main(String[] args) {
    PrinterResource p1 = new PrinterResource();
    p1.setName("p1");
    p1.start();

    PrinterResource p2 = new PrinterResource();
    p2.setName("p2");
    p2.start();


    PrinterResource p3 = new PrinterResource();
    p3.setName("p3");
    p3.start();


    PrinterResource p4 = new PrinterResource();
    p4.setName("p4");
    p4.start();

    try{
        p1.join();
        p2.join();
        p3.join();
        p4.join();
    }catch(InterruptedException e){
        e.printStackTrace();
    }
    System.out.println("Final size of arraylist  "+arrayList.size());
   }
}


답변