>  기사  >  Java  >  Java 스레드 간 통신의 대기/통지 인스턴스에 대한 자세한 설명

Java 스레드 간 통신의 대기/통지 인스턴스에 대한 자세한 설명

怪我咯
怪我咯원래의
2017-06-30 10:48:541445검색

아래 편집기에서는 Java 스레드 간의 대기/알림 통신에 대해 간략하게 설명합니다. 편집자님이 꽤 좋다고 생각하셔서 지금 공유하고 모두에게 참고용으로 드리고자 합니다. 편집기를 따라가서 살펴보겠습니다. Java의 Wait/notify/notifyAll은 Object 클래스의 메소드로, 플랫폼과 관련되어 일반적으로 사용됩니다. 생산자/소비자 패턴을 구현하는 데 사용됩니다. 먼저 관련 정의를 살펴보겠습니다.

wait():

이 메서드를 호출하는 스레드는 다른 스레드의 알림이나 중단을 기다린 후에만 WATTING 상태로 전환됩니다. ) 메서드를 사용하면 개체 잠금이 해제됩니다. 대기(긴):

최대 밀리초 동안 기다립니다. 알림이 없으면 시간이 초과되어 반환됩니다. notify():

wait() 메서드에서 개체가 반환되기를 기다리는 스레드에 알리며 반환의 전제는 스레드가 개체의 잠금을 획득한다는 것입니다. notifyAll():

이 개체를 기다리고 있는 모든 스레드에 알립니다. 작은 예

아래층에 작은 만두 식당이 있는데, 가게에 요리사와 웨이터가 있습니다. 매번 요리를 할 때마다 한 접시씩 웨이터가 한 접시를 가져다주는 것은 너무 비효율적이고 에너지 낭비입니다. 이제 셰프가 10인분을 준비할 때마다 웨이터가 이를 큰 나무 접시에 담아 고객에게 제공한다고 가정합니다. 매일 100인분을 판매한 후 레스토랑은 문을 닫고 셰프와 웨이터는 집에 가서 휴식을 취합니다. 생각해 보세요. 이 기능을 구현하려면 대기/알림 메커니즘을 사용하지 않는 경우 가장 직접적인 방법은 웨이터가 가끔씩 주방에 가서 접시에 10인분을 가져가는 것일 수 있습니다.

이 방법에는 두 가지 큰 단점이 있습니다.

1. 웨이터가 주방에 너무 부지런히 가서 웨이터가 너무 피곤하다면 그릇을 만들 때마다 손님에게 그릇을 제공하는 것이 좋습니다. 다무 플레이트의 기능이 반영되지 않습니다. 구현 코드 수준에서 구체적인 표현은 지속적인 루프가 필요하고 프로세서 리소스를 낭비한다는 것입니다. 2. 웨이터가 오랜만에 확인하러 주방에 가면, 셰프가 이미 10인분을 만들었지만 웨이터가 이를 관찰하지 않았을 수도 있습니다.

위의 예에서는 대기/알림 메커니즘을 사용하는 것이 훨씬 더 합리적입니다. 요리사가 10인분을 만들 때마다 "만두가 준비되었으니 가져가셔도 됩니다."라고 외칠 것입니다. 웨이터는 알림을 받으면 주방으로 가서 손님에게 만두를 제공합니다. 요리사는 아직 충분히 요리하지 않았습니다. 즉, 요리사로부터 알림을받지 못하여 잠시 휴식을 취할 수 있습니다. 그는 여전히 귀를 열고 셰프의 알림을 기다려야 합니다.

package ConcurrentTest;

import thread.BlockQueue;

/**
 * Created by chengxiao on 2017/6/17.
 */
public class JiaoziDemo {
  //创建个共享对象做监视器用
  private static Object obj = new Object();
  //大木盘子,一盘最多可盛10份饺子,厨师做满10份,服务员就可以端出去了。
  private static Integer platter = 0;
  //卖出的饺子总量,卖够100份就打烊收工
  private static Integer count = 0;

  /**
   * 厨师
   */
  static class Cook implements Runnable{
    @Override
    public void run() {
      while(count<100){
        synchronized (obj){
          while (platter<10){
            platter++;
          }
          //通知服务员饺子好了,可以端走了
          obj.notify();
          System.out.println(Thread.currentThread().getName()+"--饺子好啦,厨师休息会儿");
        }
        try {
          //线程睡一会,帮助服务员线程抢到对象锁
          Thread.sleep(100);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      System.out.println(Thread.currentThread().getName()+"--打烊收工,厨师回家");
    }
  }

  /**
   * 服务员
   */
  static class Waiter implements Runnable{
    @Override
    public void run() {
      while(count<100){
        synchronized (obj){
          //厨师做够10份了,就可以端出去了
          while(platter < 10){
            try {
              System.out.println(Thread.currentThread().getName()+"--饺子还没好,等待厨师通知...");
              obj.wait();
              BlockQueue
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
          //饺子端给客人了,盘子清空
          platter-=10;
          //又卖出去10份。
          count+=10;
          System.out.println(Thread.currentThread().getName()+"--服务员把饺子端给客人了");
        }
      }
      System.out.println(Thread.currentThread().getName()+"--打烊收工,服务员回家");

    }
  }
  public static void main(String []args){
    Thread cookThread = new Thread(new Cook(),"cookThread");
    Thread waiterThread = new Thread(new Waiter(),"waiterThread");
    cookThread.start();
    waiterThread.start();
  }
}

작업 결과

cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--饺子还没好,等待厨师通知...
cookThread--饺子好啦,厨师休息会儿
waiterThread--服务员把饺子端给客人了
waiterThread--打烊收工,服务员回家
cookThread--打烊收工,厨师回家

작업 메커니즘

대기/알림

의 작업 메커니즘을 이해하려면 "The Art of Concurrency

Programming

"의 그림을 빌려보세요. 누군가 내가 소위 모니터 및 개체 잠금에 대해 잘 모릅니다. 다음은 간단한 설명입니다.

jvm은 잠금을 각 개체 및 클래스와 연결합니다. 개체를 잠그는 것은 개체를 획득하는 것을 의미합니다. 객체 잠금이 획득된 경우에만 모니터를 얻을 수 있습니다. 잠금 획득에 실패하면 스레드가 차단 대기열에 들어가고 객체 잠금이 성공적으로 획득되면 wait() 메서드를 사용하여 대기할 수도 있습니다. 이때 잠금이 해제되어 대기 대기열에 들어갑니다.

잠금 장치와 모니터의 차이점에 대해 매우 자세하고 철저한 기사가 있습니다. 여기에 인용하겠습니다. 관심 있는 어린이는 이에 대해 배울 수 있습니다.

잠금 장치와 모니터의 차이점에 대해 자세히 이야기해 보겠습니다._Java 동시성

위 그림을 바탕으로 구체적인 프로세스를 정리해 보겠습니다

1. 먼저 waitThread가 객체 잠금을 획득한 후 wait() 메서드를 호출합니다. 이때 대기 스레드는 객체 잠금을 포기하고 개체의 대기 대기열 WaitQueue에 진입합니다. 2.notifyThread 스레드는 개체 잠금을 포착하고 일부 작업을 수행하며 이때 대기 스레드 waitThread는 대기 대기열 WaitQueue로 이동됩니다. 큐 동기화 대기열 및 waitThread는 대기 상태에서 차단 상태로 변경됩니다. 이때 informThread는 즉시 잠금을 해제하지 않고 계속 실행되며 남은 작업을 완료한 후 잠금을 해제합니다.

3. waitThread는 객체 잠금을 다시 획득하고 wait() 메서드에서 반환됩니다. 후속 작업을 계속 수행합니다.

4. 대기/알림 메커니즘을 기반으로 하는 스레드 간 통신 프로세스가 종료됩니다.

notifyAll의 경우 두 번째 단계에서는 대기 대기열의 모든 스레드가 동기화 대기열로 이동됩니다.

함정을 피하세요

wait/notify/notifyAll을 사용할 때 주의해야 할 몇 가지 특별한 사항이 있습니다. 요약은 다음과 같습니다.

1 반드시 동기화에서 wait()/notify()/notifyAll()을 사용하세요. 모니터는 잠금이 잠긴 후에만 획득할 수 있으므로 앞에서 언급한 것처럼 먼저 잠금을 획득해야 합니다. 그렇지 않으면 jvm도 IllegalMonitorStateException을 발생시킵니다.

2. wait()를 사용할 때 스레드가 대기 상태에 진입했는지 여부를 결정하는 조건은 if 대신 while을 사용해야 합니다. 대기 중인 스레드가 실수로 깨어날 수 있으므로 대기 전과 후에 while 루프를 사용해야 합니다. 안전을 위해 기상 조건이 충족되었는지 확인하세요.

3. inform() 또는 informAll() 메서드가 호출된 후에는 스레드가 즉시 잠금을 해제하지 않습니다. 호출은 대기 중인 스레드를 동기화 대기열로만 이동합니다. 즉, 스레드 상태가 대기에서 차단됨으로 변경됩니다.

4 wait() 메서드에서 반환한다는 전제는 스레드가 동기화 대기열을 다시 얻는다는 것입니다. 호출 객체의 잠금.

Postscript

실제 사용에서는 위에서 언급한 사항에 특히 주의하세요. 그러나 일반적으로 Inter-thread를 완료하려면 wait/notify/notifyAll을 사용합니다. Java 동시성 패키지는 이미 기회가 있을 때 나중에 자세히 소개할 다양한 BlockingQueue 등과 같은 우수하고 정교한 도구를 많이 제공하기 때문에 생산자/소비자 모델에 대한 기회는 많지 않습니다.

위 내용은 Java 스레드 간 통신의 대기/통지 인스턴스에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.