Java에서는 wait, notify 및 notifyAll을 사용하여 스레드 간 통신을 구현할 수 있습니다. . 예를 들어, Java 프로그램에 생산자와 소비자라는 두 개의 스레드가 있는 경우 대기열 버퍼에 소비할 콘텐츠가 있으므로 생산자는 소비자에게 데이터 소비를 시작하라고 알릴 수 있습니다(null용 아님). 따라서 소비자는 일부 데이터를 소비한 후 버퍼가 더 이상 가득 차지 않으므로 더 많은 데이터 생성을 시작할 수 있음을 생산자에게 알릴 수 있습니다.
멀티스레딩에서 자주 사용되는 예약어인 wait,notify,notifyAll은 실제 개발 중에는 심각하게 받아들여지지 않는 경우가 많습니다. 이 문서에서는 이러한 키워드의 사용에 대해 설명합니다.
Java에서는 wait, inform 및 informAll을 사용하여 스레드 간 통신을 달성할 수 있습니다. . 예를 들어, Java 프로그램에 생산자와 소비자라는 두 개의 스레드가 있는 경우 대기열 버퍼에 소비할 콘텐츠가 있으므로 생산자는 소비자에게 데이터 소비를 시작하라고 알릴 수 있습니다(null용 아님). 따라서 소비자는 일부 데이터를 소비한 후 버퍼가 더 이상 가득 차지 않으므로 더 많은 데이터 생성을 시작할 수 있음을 생산자에게 알릴 수 있습니다.
wait()를 사용하여 특정 조건에서 스레드를 일시 중지할 수 있습니다. 예를 들어 생산자-소비자 모델에서 생산자 스레드는 버퍼가 가득 차고 소비자 스레드가 비어 있으면 일시 중지되어야 합니다. 일부 스레드가 특정 조건이 트리거될 때까지 기다리고 있는 경우 해당 조건이 true일 때 inform 및 informAll을 사용하여 대기 중인 스레드에 다시 실행을 시작하도록 알릴 수 있습니다. 차이점은 알림은 하나의 스레드에만 알리고 어떤 스레드가 알림을 받을지 알 수 없는 반면, informAll은 대기 중인 모든 스레드에 알린다는 것입니다. 즉, 세마포어를 기다리는 스레드가 하나만 있으면 통지 및 통지All 모두 이 스레드에 알립니다. 그러나 여러 스레드가 이 세마포어를 기다리고 있는 경우 통지는 그 중 하나만 통지하고 다른 스레드는 알림을 받지 않으며 통지All은 대기 중인 모든 스레드를 깨울 것입니다.
이 기사에서는 생산자-소비자 문제를 해결하기 위해 스레드 간 통신을 구현하기 위해 wait, inform 및 informAll을 사용하는 방법을 배웁니다. Java의 멀티 스레드 동기화 문제에 대해 자세히 알아보려면 Brian Goetz의 "Java Concurrency in Practice | Java Concurrency Practice"를 읽어 보시기 바랍니다. 이 책을 읽지 않으면 Java 멀티 스레딩 여정이 완성되지 않습니다. 이 책은 Java 개발자에게 제가 가장 추천하는 책 중 하나입니다.
Wait 사용법
wait와 알림의 개념은 매우 기본적이고 둘 다 Object 클래스의 기능이지만, 이를 사용하여 코드를 작성하는 것은 쉽지 않습니다. 그들을. . 면접 때 후보자들에게 직접 코드를 작성하라고 하고, 생산자-소비자 문제를 해결하기 위해 대기 및 알림을 사용한다면 대부분 당황하거나 동기화 키워드를 잘못 사용하는 등의 실수를 저지를 것이라고 거의 확신합니다. 올바른 개체에 대해 대기를 사용하지 않거나 표준 코드 접근 방식을 따르지 않습니다. 솔직히 이 문제는 자주 사용하지 않는 프로그래머에게는 참으로 골치 아픈 문제입니다.
첫 번째 질문은 코드에서 wait()를 어떻게 사용하는가입니다. wait()는 Thread 클래스 아래의 함수가 아니기 때문에 Thread.call()을 사용할 수 없습니다. 실제로 많은 Java 프로그래머는 Thread.sleep() 사용에 익숙하기 때문에 이와 같이 작성하는 것을 좋아하므로 동일한 목적을 달성하기 위해 wait()를 사용하려고 시도하지만 곧 이것이 문제를 해결하지 못한다는 것을 알게 될 것입니다. 원활하게 문제. 올바른 방법은 여러 스레드 간에 공유되는 개체에 대해 대기를 사용하는 것입니다. 생산자-소비자 문제에서 이 공유 객체는 버퍼 큐입니다.
두 번째 질문은 동기화된 함수나 객체에서 wait를 호출해야 하는데 어떤 객체를 동기화해야 하는가입니다. 대답은 잠그려는 개체, 즉 여러 스레드에서 공유되는 개체를 동기화해야 한다는 것입니다. 생산자-소비자 문제에서 동기화되어야 하는 것은 버퍼 큐입니다. (원문에 뭔가 문제가 있는 것 같아요... 문장 끝에 물음표가 있으면 안 되고, 그렇지 않으면 말이 안 돼요...)
은 If 문이 아닌 항상 대기 및 알림 루프에서 호출됩니다
现在你知道wait应该永远在被synchronized的背景下和那个被多线程共享的对象上调用,下一个一定要记住的问题就是,你应该永远在 while循环,而不是if语句中调用wait。因为线程是在某些条件下等待的——在我们的例子里,即“如果缓冲区队列是满的话,那么生产者线程应该等 待”,你可能直觉就会写一个if语句。但if语句存在一些微妙的小问题,导致即使条件没被满足,你的线程你也有可能被错误地唤醒。所以如果你不在线程被唤 醒后再次使用while循环检查唤醒条件是否被满足,你的程序就有可能会出错——例如在缓冲区为满的时候生产者继续生成数据,或者缓冲区为空的时候消费者 开始小号数据。所以记住,永远在while循环而不是if语句中使用wait!我会推荐阅读《Effective Java》,这是关于如何正确使用wait和notify的最好的参考资料。
基于以上认知,下面这个是使用wait和notify函数的规范代码模板:
// The standard idiom for calling the wait method in Java synchronized (sharedObject) { while (condition) { sharedObject.wait(); // (Releases lock, and reacquires on wakeup) } // do action based upon condition e.g. take or put into queue }
就像我之前说的一样,在while循环里使用wait的目的,是在线程被唤醒的前后都持续检查条件是否被满足。如果条件并未改变,wait被调用之前notify的唤醒通知就来了,那么这个线程并不能保证被唤醒,有可能会导致死锁问题。
Java wait(), notify(), notifyAll() 范例
下面我们提供一个使用wait和notify的范例程序。在这个程序里,我们使用了上文所述的一些代码规范。我们有两个线程,分别名为 PRODUCER(生产者)和CONSUMER(消费者),他们分别继承了了Producer和Consumer类,而Producer和 Consumer都继承了Thread类。Producer和Consumer想要实现的代码逻辑都在run()函数内。Main线程开始了生产者和消费 者线程,并声明了一个LinkedList作为缓冲区队列(在Java中,LinkedList实现了队列的接口)。生产者在无限循环中持续往 LinkedList里插入随机整数直到LinkedList满。我们在while(queue.size == maxSize)循环语句中检查这个条件。请注意到我们在做这个检查条件之前已经在队列对象上使用了synchronized关键词,因而其它线程不能在 我们检查条件时改变这个队列。如果队列满了,那么PRODUCER线程会在CONSUMER线程消耗掉队列里的任意一个整数,并用notify来通知 PRODUCER线程之前持续等待。在我们的例子中,wait和notify都是使用在同一个共享对象上的。
import java.util.LinkedList; import java.util.Queue; import java.util.Random; /** * Simple Java program to demonstrate How to use wait, notify and notifyAll() * method in Java by solving producer consumer problem. * * @author Javin Paul */ public class ProducerConsumerInJava { public static void main(String args[]) { System.out.println("How to use wait and notify method in Java"); System.out.println("Solving Producer Consumper Problem"); Queue<Integer> buffer = new LinkedList<>(); int maxSize = 10; Thread producer = new Producer(buffer, maxSize, "PRODUCER"); Thread consumer = new Consumer(buffer, maxSize, "CONSUMER"); producer.start(); consumer.start(); } } /** * Producer Thread will keep producing values for Consumer * to consumer. It will use wait() method when Queue is full * and use notify() method to send notification to Consumer * Thread. * * @author WINDOWS 8 * */ class Producer extends Thread { private Queue<Integer> queue; private int maxSize; public Producer(Queue<Integer> queue, int maxSize, String name){ super(name); this.queue = queue; this.maxSize = maxSize; } @Override public void run() { while (true) { synchronized (queue) { while (queue.size() == maxSize) { try { System.out .println("Queue is full, " + "Producer thread waiting for " + "consumer to take something from queue"); queue.wait(); } catch (Exception ex) { ex.printStackTrace(); } } Random random = new Random(); int i = random.nextInt(); System.out.println("Producing value : " + i); queue.add(i); queue.notifyAll(); } } } } /** * Consumer Thread will consumer values form shared queue. * It will also use wait() method to wait if queue is * empty. It will also use notify method to send * notification to producer thread after consuming values * from queue. * * @author WINDOWS 8 * */ class Consumer extends Thread { private Queue<Integer> queue; private int maxSize; public Consumer(Queue<Integer> queue, int maxSize, String name){ super(name); this.queue = queue; this.maxSize = maxSize; } @Override public void run() { while (true) { synchronized (queue) { while (queue.isEmpty()) { System.out.println("Queue is empty," + "Consumer thread is waiting" + " for producer thread to put something in queue"); try { queue.wait(); } catch (Exception ex) { ex.printStackTrace(); } } System.out.println("Consuming value : " + queue.remove()); queue.notifyAll(); } } } }
为了更好地理解这个程序,我建议你在debug模式里跑这个程序。一旦你在debug模式下启动程序,它会停止在PRODUCER或者 CONSUMER线程上,取决于哪个线程占据了CPU。因为两个线程都有wait()的条件,它们一定会停止,然后你就可以跑这个程序然后看发生什么了 (很有可能它就会输出我们以上展示的内容)。你也可以使用Eclipse里的Step into和Step over按钮来更好地理解多线程间发生的事情。
本文重点:
1. 你可以使用wait和notify函数来实现线程间通信。你可以用它们来实现多线程(>3)之间的通信。
2. 永远在synchronized的函数或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。
3. 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。
4. 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。
5. 基于前文提及的理由,更倾向用 notifyAll(),而不是 notify()。
这是关于Java里如何使用wait, notify和notifyAll的所有重点啦。你应该只在你知道自己要做什么的情况下使用这些函数,不然Java里还有很多其它的用来解决同步问题的方 案。例如,如果你想使用生产者消费者模型的话,你也可以使用BlockingQueue,它会帮你处理所有的线程安全问题和流程控制。如果你想要某一个线 程等待另一个线程做出反馈再继续运行,你也可以使用CycliBarrier或者CountDownLatch。如果你只是想保护某一个资源的话,你也可 以使用Semaphore。
相关文章:
Java 동시성에서 스레드 간 협업의 두 가지 방법: 대기, 통지, 통지All 및 조건
notify()와 informAll()의 본질적인 차이점을 예시를 통해 토론
위 내용은 wait, inform 및 informAll의 올바른 사용법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!