(三)(2)wait/notify实现生产者-消费者模型,join方法

生产者,消费者模型

举个例子来说明,厨师,服务员,厨师做菜,服务员上菜,如果厨师没有做好菜,那么服务员就无法上菜,厨师做好了菜,然后通知服务员消费(上菜)。在这个过程之中,厨师扮演的就是生产者,服务员扮演消费者。

一句话说:生产者没有生产出来东西,消费者就必须等待着,生产者生产出来了,就通知消费者进行消费。

很明显,消费者等待就对应我们的wait方法,生产者通知消费者对应notify方法,所以,生产者,消费者模型可以用notify,wait来实现。

那么与之对应的,生产者不可能只有一个,消费者也不可能只有一个,所以,他们之间就有了一对一,一对多,多对一,多对多关系。

一对一模型:

如下就是一个简单的一个生产者,一个消费者的模型:

MyList.java,用于存储数据

Produce.java:生产者

package 第三章_wait_join;

//生产者
public class Produce {
    private String lock;
    public Produce(String lock){
        this.lock=lock;
    }
    public void setValue(){
        try{
            synchronized (lock){
                if(MyList.getSize()!=0){   //无需生产
                    lock.wait();
                }
                //生产
                String temp = "设置了值"+System.currentTimeMillis();
                System.out.println(temp);
                MyList.list.add(temp);
                lock.notify();  //通知消费者
            }

        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

Reduce.java,消费者

package 第三章_wait_join;

//消费者
public class Reduce {
    private String lock;
    public Reduce(String lock){
        this.lock=lock;
    }
    public void getValue(){
        try{
            synchronized (lock){
                if(MyList.getSize()==0){   //等待生产
                    lock.wait();
                }
                //消费
                System.out.println("获取到的值是:"+MyList.list.remove(0));
                lock.notify();  //通知消费者
            }

        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

ThreadP生产线程:

package 第三章_wait_join;

public class ThreadP extends Thread{
    private Produce produce;
    private Produce p;
    public ThreadP(Produce p){
        this.produce=p;
    }
    public void run(){
        while(true){
            produce.setValue();
        }
    }
}

ThreadR 消费线程:

package 第三章_wait_join;

//消费者
public class Reduce {
    private String lock;
    public Reduce(String lock){
        this.lock=lock;
    }
    public void getValue(){
        try{
            synchronized (lock){
                if(MyList.getSize()==0){   //等待生产
                    lock.wait();
                }
                //消费
                System.out.println("消费者"+Thread.currentThread().getName()+"获取到的值是:"+MyList.list.remove(0));
                MyList.list.remove(0);
                lock.notify();  //通知消费者
            }

        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

test.java.

package 第三章_wait_join;

import java.sql.ResultSet;

public class test {
    public static void main(String[] args){
        try {
            String lock = "lock";
            Produce p = new Produce(lock);
            Reduce r = new Reduce(lock);
            ThreadP threadP = new ThreadP(p);
            ThreadR threadR = new ThreadR(r);
            threadP.start();
            threadR.start();
            ThreadR.sleep(10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

运行结果:

可以看到,符合预期,生产一个,消费一个,轮换进行。

多生产,多消费情况:

问题:这种模式如果按照我们上面的写法,会出现一个问题,就是可能现在既有生产者,也有消费者在等待,这时候如果某一方发出了通知,比如生产者生产好了,发出通知,按道理是应该唤醒一个消费者的,但是也有可能唤醒其他生产者,而不是消费者,那这样很明显是不合理的,如果这样的次数多了,就会导致所有生产者和消费者都处于wait状态,那么这个时候程序就会出现假死状态。比如下将test.java更改为下面的代码,运行之后会发现程序运行一会就处于”假死”状态了。

package 第三章_wait_join;

import java.sql.ResultSet;

public class test {
    public static void main(String[] args){
        try {
            String lock = "lock";
            Produce p = new Produce(lock);
            Reduce r = new Reduce(lock);
            ThreadP[] threadP = new ThreadP[2];
            ThreadR[] threadR = new ThreadR[2];
            for(int i=0;i<2;i++){
                threadP[i] = new ThreadP(p);
                threadP[i].setName("生产者:"+i);
                threadR[i] = new ThreadR(r);
                threadR[i].setName("消费者:"+i);
                threadP[i].start();
                threadR[i].start();
            }
            ThreadR.sleep(10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

解决办法:

使用notifyAll()  替换 notify每次将所有线程都唤醒,包括自己的异类,这样就能保证生产的都被消费,消费者都有所消费,不会出现上述状况。

一个生产者,多消费:

问题:这种情况下,会出现两个问题。

1.条件改变时,其他消费者没有及时得到响应,以为可以消费,继续执行下去,这个时候如果是一个数组之类的,就会出现越界的异常,,,,这个没太看懂。

2.和多生产多消费一样,消费者唤醒时候可能唤醒同类,同类没有可以消费的,继续等待,下一个同类也没有可以消费的,继续等待,最后导致全部同类等待,生产者也等待,没有能够notify的,自然假死,

解决:

1.将判断条件是否成立的语句改为while循环进行判断

2.notifyAll()唤醒所有线程

多生产,一个消费:

这种一般没有什么问题,不会假死,因为就算生产者唤醒了生产者,然后发现还没有被消费,又等待,以此类推,所有生产者都等待了,但是最后总会唤醒消费者,消费者总能消费,消费之后总能唤醒一个生产者进行生产。

Join:

这个方法是调用该方法的线程执行完毕之后,当前线程再执行接下来的语句,有下面几个特点:

1.会释放锁,换到另一个线程执行,相比之下sleep并不会释放锁。原因是join内部是使用wait实现的,wait会释放锁

2.和wait一样,已经处于阻塞状态了,就不能再用其他方法让该线程处于阻塞状态

3.join(long)   long时间之后,如果目标线程还没有执行完,则取消阻塞状态,进入就绪状态

4.同时使用sleep和join(long)可能会出现join后面的代码提前之前,原因是可能join(long)在竞争锁的过程之中,没有竞争过其他线程,其他线程之中使用了slee(long2)long2 > long,这就意味着join已经过了有效时间,所以不再阻塞,而是和调用join的线程竞争,竞争自然可能有很多结果了

。。不放代码了,麻烦

原文地址:https://www.cnblogs.com/eenio/p/11385090.html

时间: 2024-07-30 03:24:04

(三)(2)wait/notify实现生产者-消费者模型,join方法的相关文章

Java多线程之~~~~使用wait和notify实现生产者消费者模型

在多线程开发中,最经典的一个模型就是生产者消费者模型,他们有一个缓冲区,缓冲区有最大限制,当缓冲区满 的时候,生产者是不能将产品放入到缓冲区里面的,当然,当缓冲区是空的时候,消费者也不能从中拿出来产品,这就 涉及到了在多线程中的条件判断,java为了实现这些功能,提供了wait和notify方法,他们可以在线程不满足要求的时候 让线程让出来资源等待,当有资源的时候再notify他们让他们继续工作,下面我们用实际的代码来展示如何使用wait和 notify来实现生产者消费者这个经典的模型. 首先是

生产者/消费者模型

http://www.cnblogs.com/xrq730/p/4855663.html 什么是生产者/消费者模型 一种重要的模型,基于等待/通知机制.生产者/消费者模型描述的是有一块缓冲区作为仓库,生产者可将产品放入仓库,消费者可以从仓库中取出产品,生产者/消费者模型关注的是以下几个点: 1.生产者生产的时候消费者不能消费 2.消费者消费的时候生产者不能生产 3.缓冲区空时消费者不能消费 4.缓冲区空时生产者不能生产 生产者/模型作为一种重要的模型,它的优点在于: 1.解耦.因为多了一个缓冲区

Java多线程14:生产者/消费者模型

什么是生产者/消费者模型 一种重要的模型,基于等待/通知机制.生产者/消费者模型描述的是有一块缓冲区作为仓库,生产者可将产品放入仓库,消费者可以从仓库中取出产品,生产者/消费者模型关注的是以下几个点: 1.生产者生产的时候消费者不能消费 2.消费者消费的时候生产者不能生产 3.缓冲区空时消费者不能消费 4.缓冲区满时生产者不能生产 生产者/模型作为一种重要的模型,它的优点在于: 1.解耦.因为多了一个缓冲区,所以生产者和消费者并不直接相互调用,这一点很容易想到,这样生产者和消费者的代码发生变化,

生产者消费者模型的正确姿势

简介: 生产者.消费者模型是多线程编程的常见问题,最简单的一个生产者.一个消费者线程模型大多数人都能够写出来,但是一旦条件发生变化,我们就很容易掉进多线程的bug中.这篇文章主要讲解了生产者和消费者的数量,商品缓存位置数量,商品数量等多个条件的不同组合下,写出正确的生产者消费者模型的方法. 欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 定义商品类 package demo; /*定义商品*/ public class Goods

python2.0_s12_day9之day8遗留知识(queue队列&amp;生产者消费者模型)

4.线程 1.语法 2.join 3.线程锁之Lock\Rlock\信号量 4.将线程变为守护进程 5.Event事件 * 6.queue队列 * 7.生产者消费者模型 4.6 queue队列 queue非常有用,当信息必须安全的在多个线程之间进行数据交换的时候就应该想到queue 所以,queue它能保证数据被安全的在多个线程之间进行交换,那他就是天生的线程安全. queue有那么几种: class queue.Queue(maxsize=0) # 先入先出 class queue.LifoQ

java多线程15 :wait()和notify() 的生产者/消费者模式

什么是生产者/消费者模型 一种重要的模型,基于等待/通知机制.生产者/消费者模型描述的是有一块缓冲区作为仓库,生产者可将产品放入仓库,消费者可以从仓库中取出产品,生产者/消费者模型关注的是以下几个点: 1.生产者生产的时候消费者不能消费 2.消费者消费的时候生产者不能生产 3.缓冲区空时消费者不能消费 4.缓冲区满时生产者不能生产 生产者/模型作为一种重要的模型,它的优点在于: 1.解耦.因为多了一个缓冲区,所以生产者和消费者并不直接相互调用,这一点很容易想到,这样生产者和消费者的代码发生变化,

生产者消费者模型——wait/notify/notifyAll使用

告警系统架构如下 1. 数据处理系统处理完原始数据并入库后,发送消息到kafka系统: 2. 告警生产者从kafka系统查询消息存入告警消息队列: 3. 告警消费者从告警消息队列查询消息进行处理. 这显然是生产者消费者模型,一个告警消息生产者,多个告警消息消费者.生产者生产消息过快会产生消息积压,生产者生产消息过慢不能充分利用硬件资源.所以必须要生产者和消费者协同处理,使得系统充分利用.具体做法是消息队列为空时,消费者通知生产者生产消息,生产者生产好消息后,通知消费者处理消息.Java中我们使用

多线程学习-基础(十二)生产者消费者模型:wait(),sleep(),notify()实现

一.多线程模型一:生产者消费者模型   (1)模型图:(从网上找的图,清晰明了) (2)生产者消费者模型原理说明: 这个模型核心是围绕着一个"仓库"的概念,生产者消费者都是围绕着:"仓库"来进行操作,一个仓库同时只能被一个生产者线程或一个消费者线程所操作,synchronized锁住的也是这个仓库,仓库是一个容器,所以会有边界值,0和仓库可存放上限,在这个上限内,可以设置多种级别,不同的级别可以执行不同的策略流程. (3)本案例使用知识点: Thread.curre

Python之路(第三十八篇) 并发编程:进程同步锁/互斥锁、信号量、事件、队列、生产者消费者模型

一.进程锁(同步锁/互斥锁) 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理. 例子 #并发运行,效率高,但竞争同一打印终端,带来了打印错乱 from multiprocessing import Process import os,time def work(): print('%s is running' %os.getpid()) time.sleep(2) print('