>  기사  >  Java  >  Java 스레드 간 통신 대기/알림

Java 스레드 간 통신 대기/알림

巴扎黑
巴扎黑원래의
2017-06-26 11:38:571454검색

 Java의 Wait/notify/notifyAll은 스레드 간 통신을 구현하는 데 사용할 수 있으며 Object 클래스의 메서드입니다. 이 세 가지 메서드는 모두 기본 메서드이며 생산자/소비자 모델을 구현하는 데 자주 사용됩니다. . 먼저 관련 정의를 살펴보겠습니다.

   wait(): 이 메서드를 호출하는 스레드는 WATTING 상태에 들어가고 다른 스레드의 알림이나 중단을 기다리는 경우에만 반환됩니다. 메서드를 사용하면 개체가 잠금 해제됩니다.

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

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

  notifyAll(): 이 개체를 기다리고 있는 모든 스레드에 알립니다.

작은 예

아래층에 작은 만두 식당이 있습니다. 가게에는 요리사가 요리하는 것을 막기 위해 웨이터가 있습니다. 웨이터가 한 접시를 가져왔는데 너무 비효율적이고 에너지 낭비였습니다. 이제 셰프가 10인분을 준비할 때마다 웨이터가 이를 큰 나무 접시에 담아 고객에게 제공한다고 가정합니다. 매일 100인분을 판매한 후 레스토랑은 문을 닫고 셰프와 웨이터는 집에 가서 휴식을 취합니다.

생각해 보세요. 이 기능을 구현하기 위해 대기/알림 메커니즘을 사용하지 않는 경우 가장 직접적인 방법은 웨이터가 가끔씩 주방에 가서 접시에 담긴 접시를 꺼내는 것일 수 있습니다. 10인분 후. 이 방법에는 두 가지 큰 단점이 있습니다.

  1. 웨이터가 주방에 너무 부지런히 가서 웨이터가 너무 피곤하다면 그릇을 만들 때마다 손님에게 그릇을 제공하는 것이 더 좋고, 큰 나무 접시는 반사될 수 없습니다. 구현 코드 수준에서 구체적인 표현은 지속적인 루프가 필요하고 프로세서 리소스를 낭비한다는 것입니다.

  2. 웨이터가 오랜만에 확인하러 주방에 가면 적시성은 보장할 수 없습니다. 어쩌면 셰프가 이미 10인분을 만들었지만 웨이터가 이를 관찰하지 않았을 수도 있습니다.

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

 

 1 package ConcurrentTest; 2  3 import thread.BlockQueue; 4  5 /** 6  * Created by chengxiao on 2017/6/17. 7  */ 8 public class JiaoziDemo { 9     //创建个共享对象做监视器用10     private static Object obj = new Object();11     //大木盘子,一盘最多可盛10份饺子,厨师做满10份,服务员就可以端出去了。12     private static Integer platter = 0;13     //卖出的饺子总量,卖够100份就打烊收工14     private static Integer count = 0;15 16     /**17      * 厨师18      */19     static class Cook implements Runnable{20         @Override21         public void run() {22             while(count<100){23                 synchronized (obj){24                     while (platter<10){25                         platter++;26                     }27                     //通知服务员饺子好了,可以端走了28                     obj.notify();29                     System.out.println(Thread.currentThread().getName()+"--饺子好啦,厨师休息会儿");30                 }31                 try {32                     //线程睡一会,帮助服务员线程抢到对象锁33                     Thread.sleep(100);34                 } catch (InterruptedException e) {35                     e.printStackTrace();36                 }37             }38             System.out.println(Thread.currentThread().getName()+"--打烊收工,厨师回家");39         }40     }41 42     /**43      * 服务员44      */45     static class Waiter implements Runnable{46         @Override47         public void run() {48             while(count<100){49                 synchronized (obj){50                     //厨师做够10份了,就可以端出去了51                     while(platter < 10){52                         try {53                             System.out.println(Thread.currentThread().getName()+"--饺子还没好,等待厨师通知...");54                             obj.wait();55                             BlockQueue56                         } catch (InterruptedException e) {57                             e.printStackTrace();58                         }59                     }60                     //饺子端给客人了,盘子清空61                     platter-=10;62                     //又卖出去10份。63                     count+=10;64                     System.out.println(Thread.currentThread().getName()+"--服务员把饺子端给客人了");65                 }66             }67             System.out.println(Thread.currentThread().getName()+"--打烊收工,服务员回家");68 69         }70     }71     public static void main(String []args){72         Thread cookThread = new Thread(new Cook(),"cookThread");73         Thread waiterThread = new Thread(new Waiter(),"waiterThread");74         cookThread.start();75         waiterThread.start();76     }77 }
작은 예

실행 결과

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--打烊收工,厨师回家
실행 결과

작동 메커니즘

대기/알림의 작동 메커니즘을 이해하려면 "동시 프로그래밍의 기술"의 그림을 빌려보세요.

일부 사람들은 소위 모니터 및 개체 잠금에 대해 잘 알지 못할 수도 있습니다. 여기에 간단한 설명이 있습니다. 설명:

  jvm은 각 개체 및 클래스에 잠금을 연결합니다. 개체를 잠그는 것은 개체와 연결된 모니터를 얻는 것을 의미합니다.

 객체 잠금을 획득한 경우에만 모니터를 얻을 수 있습니다. 잠금 획득에 실패하면 스레드가 차단 대기열에 들어가고, 객체 잠금이 성공적으로 획득되면 wait() 메서드를 사용할 수도 있습니다. 모니터에서 대기하면 잠금이 해제되어 대기 대기열에 들어갑니다.

  자물쇠와 모니터의 차이점에 관해 정원의 친구가 매우 자세하고 철저한 기사를 여기에서 인용했습니다. 관심 있는 어린이는 자물쇠와 모니터의 차이점에 대해 배울 수 있습니다 - Java Concurrency

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

  1. 먼저 waitThread가 객체 잠금을 획득한 후 wait() 메서드를 호출합니다. 이때 대기 스레드는 포기합니다. object lock, 동시에 개체의 대기 대기열 WaitQueue;에 진입합니다.

  2. informThread 스레드는 개체 잠금을 확보하고 몇 가지 작업을 수행하며 inform() 메서드를 호출합니다. 대기 중인 스레드 waitThread는 대기 중인 큐인 WaitQueue에서 동기화로 이동됩니다. 동기화된 큐 큐에서 waitThread는 대기 상태에서 차단된 상태로 변경됩니다. notifyThread는 현재 잠금을 즉시 해제하지 않는다는 점에 유의해야 합니다. 계속 실행되며 나머지 작업을 완료한 후 잠금이 해제됩니다.

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

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

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

함정을 피하세요

wait/notify/notifyAll을 사용할 때 주의해야 할 몇 가지 특별한 사항이 있습니다. 여기에 요약해 보겠습니다.

   1을 사용하세요. 즉, 잠금을 먼저 획득해야 합니다. 잠금이 설정된 후에만 모니터를 획득할 수 있기 때문입니다. 그렇지 않으면 jvm도 IllegalMonitorStateException을 발생시킵니다.

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

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

Postscript  wait/notify 관련 내용 소개입니다. 실제 사용시에는 위에서 언급한 사항에 특히 주의하지만, 일반적으로는 wait/notify/notifyAll을 직접 사용하여 인터스레드를 완성합니다. Java 동시성 패키지는 이미 기회가 있을 때 나중에 자세히 소개할 다양한 BlockingQueue 등과 같은 우수하고 정교한 도구를 많이 제공하기 때문에 생산자/소비자 모델에 대한 기회는 많지 않습니다.

 서로 격려

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

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