多线程-生产者消费者

Posted Andrew_F

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程-生产者消费者相关的知识,希望对你有一定的参考价值。

正解博客:https://blog.csdn.net/u011863767/article/details/59731447

永远在循环(loop)里调用 wait 和 notify,不是在 If 语句

现在你知道wait应该永远在被synchronized的背景下和那个被多线程共享的对象上调用,下一个一定要记住的问题就是,你应该永远在while循环,而不是if语句中调用wait。因为线程是在某些条件下等待的——在我们的例子里,即“如果缓冲区队列是满的话,那么生产者线程应该等待”,你可能直觉就会写一个if语句。但if语句存在一些微妙的小问题,导致即使条件没被满足,你的线程你也有可能被错误地唤醒。所以如果你不在线程被唤醒后再次使用while循环检查唤醒条件是否被满足,你的程序就有可能会出错——例如在缓冲区为满的时候生产者继续生成数据,或者缓冲区为空的时候消费者开始小号数据。所以记住,永远在while循环而不是if语句中使用wait!我会推荐阅读《Effective Java》,这是关于如何正确使用wait和notify的最好的参考资料。

有的这样说:(http://www.tuicool.com/articles/a6ram23)

因为在多核处理器环境中, Signal 唤醒操作可能会激活多于一个线程(阻塞在条件变量上的线程),使得多个调用等待的线程返回。所以用while循环对condition多次判断,可以避免这种假唤醒。

 

基于以上认知,下面这个是使用wait和notify函数的规范代码模板:

1
2
3
4
5
6
7
8
// 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的唤醒通知就来了,那么这个线程并不能保证被唤醒,有可能会导致死锁问题。

注意:

 

1 永远在synchronized的方法或对象里使用wait、notify和notifyAll,不然Java虚拟机会生成 IllegalMonitorStateException。

2 永远在while循环里而不是if语句下使用wait。这样,循环会在线程睡眠前后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。

3 永远在多线程间共享的对象(在生产者消费者模型里即缓冲区队列)上使用wait。

 

生产者消费者代码:

https://zhuanlan.zhihu.com/p/20300609 代码有部分问题,修改如下

ProsumerToConsumer类
public class ProsumerToConsumer {

    public static void main(String[] args) throws Exception {
        
        Person person =new Person();
        Thread t1=new Thread(new Producer(person),"生产者t1");
        Thread t2=new Thread(new Producer(person),"生产者t2");
        Thread t3=new Thread(new Producer(person),"生产者t3");
        Thread s1=new Thread(new Consumer(person), "消费者s1");
        Thread s2=new Thread(new Consumer(person), "消费者s2");
        Thread s3=new Thread(new Consumer(person), "消费者s3");

        s1.start();
        s2.start();
        Thread.sleep(2000);
        t1.start();
        t2.start();
        t3.start();
               
    }

}

  producer代码:

public class Producer implements Runnable {

    private Person person;
//    private String name;
    
     public Producer( Person person) {
         this.person=person;
//         this.name=name;
    }
     
    @Override
    public void run() {
        try {
            person.producer();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

 

  consumer代码:

public class Consumer implements Runnable{

    private Person person;
    
     public Consumer( Person person) {
         this.person=person;
    }

    @Override
    public void run() {
        
        try {
            person.consumer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

  person代码:

public class Person {

    private static volatile int num=0;
    private Object obj= new Object();
    
    private static final int MAX_NUM=5;
    
    public void producer()throws InterruptedException{
        
        while(true){
            synchronized (obj) {
                while(num==MAX_NUM) {
                    System.out.println("box is full,size = " + num);
                    obj.wait();
                }
                num++;
                System.out.println( Thread.currentThread().getName()+ num);
                obj.notifyAll();
            }
        }
    }
    
    public void consumer() throws InterruptedException{
        while (true) {
            synchronized (obj) {
                while (num==0) {
                    System.out.println("box is empty,size = " + num);
                    obj.wait();
                }
                num--;
                obj.notifyAll();
                System.out.println(Thread.currentThread().getName() + num);
            }
        }
        }
}

 

实例验证1:如果判断用的是while  数据在队列容量范围之内。

while(num==MAX_NUM)
        while(true){
            synchronized (obj) {
                while(num==MAX_NUM) {
                    System.out.println("box is full,size = " + num);
                    obj.wait();
                }

 

实例验证2:如果判断用的是if  ,数据已经超出了队列的容量

if(num==MAX_NUM) 
        while(true){
            synchronized (obj) {
                if(num==MAX_NUM) {
                    System.out.println("box is full,size = " + num);
                    obj.wait();
                }

 

以上是关于多线程-生产者消费者的主要内容,如果未能解决你的问题,请参考以下文章

LINUX多线程(生产者消费者模型,POXIS信号量)

生产者消费者中多线程安全问题(即线程间通信的安全问题)

13,多线程-生产者消费者问题2

消费者生产者多线程冻结

多线程(生产者-消费者)

多线程核心点