在java中使用wait()和notify()的简单场景

我能得到一个完整的简单场景,即教程,建议这应该如何使用,特别是与队列?

246414 次浏览

你有没有看过这个Java教程?

此外,我建议您在真正的软件中远离这类东西。使用它很好,这样您就知道它是什么,但是并发性到处都有陷阱。如果您正在为其他人构建软件,最好使用更高级别的抽象和同步集合或JMS队列。

至少我是这么做的。我不是并发专家,所以我尽量避免手工处理线程。

即使你特别要求wait()notify(),我觉得这句话仍然足够重要:

Josh Bloch, Effective Java第二版,第69项:比起waitnotify(强调他的),更喜欢并发实用程序:

考虑到正确使用__ABC0和notify的难度,你应该使用更高级别的并发实用程序[…与java.util.concurrent提供的高级语言相比,直接使用waitnotify就像用“并发汇编语言”编程。很少(如果有的话)有理由在新代码中使用__ABC0和notify

不是一个队列的例子,但非常简单:)

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(condition),因为

  • a)线程可以偶尔唤醒 从等待状态变为无存在状态 任何人都可以通知。(即使当 送披萨的没敲钟 有人会决定试试吃 披萨)。李< / >
  • b)你应该检查 条件再次获得后 同步锁。比如说披萨 不要永远存在。你保持清醒, 排队买披萨,但不是 每个人都能吃。如果你没有 检查,你可能会吃到纸!:) (也许更好的例子是 李while(!pizzaExists){ wait(); }。< / >

2)在调用wait/nofity之前,你必须持有锁(synchronized)。线程还必须在唤醒前获得锁。

3)尽量避免在同步块中获得任何锁,并努力不调用外来方法(你不确定它们在做什么的方法)。如果必须这样做,请确保采取措施避免死锁。

4)小心使用notify()。坚持使用notifyAll()直到你知道你在做什么。

5)最后,但并非最不重要的,读取Java并发性实践!

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()调用将无法进行,直到第一个线程实际阻塞。

其次,您需要将检查的条件放在while循环中,而不是if语句中,这是由于所谓的伪唤醒问题。在这里,等待线程有时可以在不调用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并发性实践,因为它涵盖了所有你想知道的与并发相关的问题和解决方案。

例子

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());
}
}

该问题要求wait() + notify()涉及队列(缓冲区)。首先想到的是使用缓冲区的生产者-消费者场景。

我们的系统有三个组成部分:

  1. Queue [Buffer] -线程间共享的固定大小的队列
  2. 生成器——线程生成/插入值到缓冲区
  3. 消费者——线程从缓冲区中消费/删除值
< p >生产者线程: 生产者在缓冲区中插入值,直到缓冲区满为止。 如果缓冲区已满,生产者调用wait()并进入等待阶段,直到消费者唤醒它
    static 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) {
if (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.notify();
}
sleepRandom();
}
}
}
< p >消费者线程: 消费者线程从缓冲区中删除值,直到缓冲区为空。 如果缓冲区为空,消费者调用wait()方法并进入等待状态,直到生产者发送通知信号
    static 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() {
Random random = new Random();
while (true) {
synchronized (queue) {
if (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(" vvv Consuming value : " + queue.remove());
queue.notify();
}
sleepRandom();
}
}
}

实效的方法:

    public static void sleepRandom(){
Random random = new Random();
try {
Thread.sleep(random.nextInt(250));
} catch (InterruptedException e) {
e.printStackTrace();
}
}

应用程序代码:

    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();
}

示例输出:

 ^^^ Producing value : 1268801606
vvv Consuming value : 1268801606
Queue is empty,Consumer thread is waiting for producer thread to put something in queue
^^^ Producing value : -191710046
vvv Consuming value : -191710046
^^^ Producing value : -1096119803
vvv Consuming value : -1096119803
^^^ Producing value : -1502054254
vvv Consuming value : -1502054254
Queue is empty,Consumer thread is waiting for producer thread to put something in queue
^^^ Producing value : 408960851
vvv Consuming value : 408960851
^^^ Producing value : 2140469519
vvv Consuming value : 65361724
^^^ Producing value : 1844915867
^^^ Producing value : 1551384069
^^^ Producing value : -2112162412
vvv Consuming value : -887946831
vvv Consuming value : 1427122528
^^^ Producing value : -181736500
^^^ Producing value : -1603239584
^^^ Producing value : 175404355
vvv Consuming value : 1356483172
^^^ Producing value : -1505603127
vvv Consuming value : 267333829
^^^ Producing value : 1986055041
Queue is full, Producer thread waiting for consumer to take something from queue
vvv Consuming value : -1289385327
^^^ Producing value : 58340504
vvv Consuming value : 1244183136
^^^ Producing value : 1582191907
Queue is full, Producer thread waiting for consumer to take something from queue
vvv Consuming value : 1401174346
^^^ Producing value : 1617821198
vvv Consuming value : -1827889861
vvv Consuming value : 2098088641