java学习笔记之多线程生产者与消费者

来源:互联网 发布:网络创业课理论与实践 编辑:程序博客网 时间:2024/06/05 01:15

  生产者-消费者问题是一个经典的线程同步问题。可以理解为:生产者生产出一个产品,将其放入缓冲区,消费者从缓冲区取走产品之后,生产者继续生产产品。注意的是,生产者必须等待消费者取走产品之后才继续生产,消费者也必须在缓冲区有产品是才消费。

一、首先,最简单的模型就是单生产者,单消费者,单次生产消费


public class Resource {private String name;private boolean flag;private int count=1;public void setResource(String name){this.name = name + "---" +count;count++;System.out.println(Thread.currentThread().getName()+"生产者"+this.name);}public void getResource(){System.out.println(Thread.currentThread().getName()+"消费者"+this.name);}}public class Producer implements Runnable {private Resource rec;  //这一步是为了保证生产者和消费者操作的资源是一样的public Producer(Resource rec){this.rec=rec;}public void run(){    rec.setResource("面包");}}public class Customer implements Runnable{private Resource res;Customer(Resource res){this.res=res;}public void run(){    res.getResource();}}public class ProducerCustomer {public static void main(String[] args) {Resource res = new Resource();Producer pro = new Producer(res);Customer cus = new Customer(res);Thread th1 = new Thread(pro);Thread th2 = new Thread(cus);th1.start();th2.start();}}

结果是:

Thread-0生产者面包---1
Thread-1消费者面包---1

意思就是生产者生产一个产品,消费者就消费一个产品


二、单生产者单消费者多次生产消费

   那么该如何实现多生产者多消费者呢?可以在生产者和消费者的run函数里面加上while(true)的判断,但是这样就会产生消费者重复消费或者生产者重复生产的问题,如下图。原因就是多线程的不安全性,那么就可以用同步来解决该问题。


   当时当给生产者和消费者加上同步之后,publicsynchronizedvoid setResource(String name);publicsynchronized void getResource(),会发现又产生了生产者持续多生产和消费者持续多消费的问题,如下图


   该问题是由于生产者或消费者不断拿到锁,可以不断生产或消费。那么可以在拿到锁之后,加上判断的一步,如果没有产品才生产,有产品才消费。这就可以引入等待唤醒机制。

wait():该方法可以让线程处于冻结状态,并将线程先临时存储到线程池
notify():唤醒指定线程池中的任意一个线程
notifyAll(): 唤醒指定线程池中的所有线程
注意:1)这些方法必须用在同步中,因为它们是同来操作同步锁上的线程状态的。
    2)在使用这些方法时,必须标识它们所属于的锁,标识的方式就是:锁对象.wait()/锁对象.notify/锁对象.notifyAll,相同锁的notify()可以获取相同锁的wait()。(wait方法等在API中查看的时候,它是属于object的方法的)

完整程序如下:


public class Resource {private String name;private boolean flag;private int count=1;public synchronized void setResource(String name){if(flag){try{wait(); //查阅API可以知道,wait方法需要抛出InterruptedException异常,在该段代码中,wait方法对应的锁是默认的this,可以不写}catch(InterruptedException e){}}this.name = name + "---" +count;count++;System.out.println(Thread.currentThread().getName()+"生产者"+this.name);flag = true;notify();}public synchronized void getResource(){if(!flag){  //在生产者或消费者拿到锁之后进行判断,是否是继续生产消费还是进入等待状态。try{wait();}catch(InterruptedException e){}} System.out.println(Thread.currentThread().getName()+"消费者"+this.name);flag=false;notify();}}public class Producer implements Runnable {private Resource rec;public Producer(Resource rec){this.rec=rec;}public void run(){while(true)     rec.setResource("面包");}}public class Customer implements Runnable{private Resource res;Customer(Resource res){this.res=res;}public void run(){while(true)    res.getResource();}}public class ProducerCustomer {public static void main(String[] args) {Resource res = new Resource();Producer pro = new Producer(res);Customer cus = new Customer(res);Thread th1 = new Thread(pro);Thread th2 = new Thread(cus);th1.start();th2.start();}}



结果如下图



三、多生产者多消费者多次生产消费

       首先,肯定是要开启多个线程来产生多个生产者和多个消费者,这样有可能会产生被唤醒的线程没有再次判断标记就开始工作,导致重复生产和重复消费的问题。所以可以把if 判断改为while判断,同时在上述代码中的notify()要改成notifyAll(),如果不改的话,会出现程序在运行过程中,所有线程都处于冻结状态,也就是死锁。(如何理解--->本方线程在唤醒时,又一次唤醒了本方线程。而本方线程循环判断标记时,又一次继续等待,导致所有线程都处于冻结状态)

       完整代码如下:


public class Resource {private String name;private boolean flag;private int count=1;public synchronized void setResource(String name){while(flag){ //循环判标记try{wait();//wait()方法}catch(InterruptedException e){}}this.name = name + "---" +count;count++;System.out.println(Thread.currentThread().getName()+"生产者"+this.name);flag = true;notifyAll();}public synchronized void getResource(){while(!flag){try{wait();}catch(InterruptedException e){}} System.out.println(Thread.currentThread().getName()+"消费者"+this.name);flag=false;notifyAll();}}public class Producer implements Runnable {private Resource rec;public Producer(Resource rec){this.rec=rec;}public void run(){while(true)    rec.setResource("面包");}}public class Customer implements Runnable{private Resource res;Customer(Resource res){this.res=res;}public void run(){while(true)    res.getResource();}}public class ProducerCustomer {public static void main(String[] args) {Resource res = new Resource();Producer pro = new Producer(res);Customer cus = new Customer(res);Thread th0 = new Thread(pro); //开启多个线程Thread th1 = new Thread(pro);Thread th2 = new Thread(cus);Thread th3 = new Thread(cus);th0.start();th1.start();th2.start();th3.start();}}


结果是


最后,可以发现,这个程序的效率较低,因为它在不断唤醒所有线程,并循环判断标记。但是实现了多生产和多消费。

0 0