理解生产者和消费者,两种实现方式

来源:互联网 发布:java自定义表单 编辑:程序博客网 时间:2024/05/03 23:07

生产者和消费者有两种实现方式:

1.加同步锁实现互斥

2.使用阻塞队列实现互斥

 

生产者消费者问题故事举例

送情报:

送者类,取者类,房间类三个类。

 

送者先判断flag(情报标志,为true时有情报,为false时情报取完为空),

flagtrue则在旁边的凉亭中(等待池,wait()方法)中等待取者取完情报,

flagfalse则送者从凉亭走到门口等房锁打开(从等待池中进入锁池),房间锁打开时(取者放完情报开锁出来了),送者进入房间并把门锁上送情报(synchronized),防止取者同时来取,放情报,把flag设置为true

放完情报出门解开房间锁(synchronized方法块执行完)出来,放烟花弹通知取者取情报(唤醒),然后躲在旁边的凉亭中(等待池,调用wait()方法)中等待取者来取情报

 

取者先判断flag(情报标志,为true时有情报,为false时情报已空),

flagfalse则躲在旁边的凉亭中(等待池,wait()方法)中等待送者放情报,

flagtrue则取者从凉亭中走到门口等房锁打开(从等待池中进入锁池),

房间锁打开时(送者送完情报开锁出来了),取者进入房间并把门锁上取情报,防止送者同时来送,取情报,把flag设置为true

取完情报解锁开门,放烟花弹通知送者送情报(唤醒),然后回到旁边的凉亭(等待池)中继续等待送者送情报



/*1.加同步锁实现生产者和消费者

*waitnotifynotifyAll方法的使用

*这几个方法必须在同一个对象的同步锁中才能使用

*每一个对象除了有一个锁之外,还有一个等待队列

*/

public class Test1

{

public static void main(String[] args)

{

Tree q=new Tree();

Producer p=new Producer(q);

Consumer c=new Consumer(q);

p.start();

c.start();

}

}

class Producer extends Thread

{

Bower q;

Producer(Bower q){this.q=q;}

public void run()

{

for(int i=0;i<10;i++)

{

q.put(i);

System.out.println("Producer put "+i);

}

}

}

class Consumer extends Thread

{

Bower q;

Consumer(Bower q)

{this.q=q;}

public void run()

{

while(true)

{System.out.println("Consumer get "+q.get());}

}

}

/* 凉亭 **/

class Bower

{

int message;//情报数量

boolean bFull=false;//情报标志,为true时有情报,为false时情报取完为空

//放情报

public synchronized void put(int i)

{

if(!this.bFull)//为空时情报已取完,需要放情报

{

this.message=i;//把情报放入房间

this.bFull=true;//设为非空

/*从该对象的等待队列中释放消费者线程进入该对象锁池

使该线程将再次成为可运行的线程*/

    this.notify();//唤醒等待池

}

try

{//生产者线程开启锁池进入this对象的等待队列

this.wait();

}

catch(Exception e)

{e.printStackTrace();}

}

//取情报

public synchronized int get()

{

if(!this.bFull)//为空时,没有情报可取,需要在等待池中等待

{

try

{

this.wait();//消费者线程开启锁池(同步块代码执行完时)

//进入this对象的等待队列

}

catch(Exception e)

{e.printStackTrace();}

}

bFull=false;//设置为空

/*从该对象的等待队列中释放生产者线程进入该对象锁池

使该线程将再次成为可运行(就绪)的线程*/

this.notify(); //唤醒

int value=message;

return value;

}

}

什么是阻塞队列?

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:

在队列为空时,获取元素的线程会等待队列变为非空。

当队列满时,存储元素的线程会等待队列可用。

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

 

阻塞队列提供了四种处理方法:

抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出IllegalStateException("Queue full")异常。当队列为空时,从队列里获取元素时会抛出NoSuchElementException异常 。

返回特殊值:插入方法会返回是否成功,成功则返回true。移除方法,则是从队列里拿出一个元素,如果没有则返回null

一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里take元素,队列也会阻塞消费者线程,直到队列可用。

超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。

 


JDK7提供了7个阻塞队列。分别是

ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。

LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。

PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。

DelayQueue:一个使用优先级队列实现的无界阻塞队列。

SynchronousQueue:一个不存储元素的阻塞队列。

LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。

LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

第二种方式使用java.util.concurrent.BlockingQueue类来重写等待队列凉亭那个类

/**

*采用阻塞队列实现互斥

*/

import java.util.concurrent.BlockingQueue;

import java.util.concurrent.ArrayBlockingQueue;  

import java.util.concurrent.LinkedBlockingQueue;  

class Producer implements Runnable {

BlockingQueue<Integer> queue;

//BlockingQueue来重写凉亭类,来自动达到所有排队方法使用内部锁定或其他形式的并发控制的目的

public Producer(BlockingQueue<Integer> queue) {

this.queue = queue;

}

public void run() {

try {

//String temp = "线程:"+ Thread.currentThread().getName();

Integer temp = 0;

System.out.println("I have made a product:"+ Thread.currentThread().getName());

queue.put(temp);//如果队列是满的话,会阻塞当前线程

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

class Consumer implements Runnable{  

    BlockingQueue<Integer> queue;

    public Consumer(BlockingQueue<Integer> queue){  

        this.queue = queue;  

    }  

    public void run() {  

        try {  

Integer temp = queue.take();//如果队列为空,会阻塞当前线程

            System.out.println("I have taken a product--------------"+ Thread.currentThread().getName());  

        } catch (InterruptedException e) {  

            e.printStackTrace();  

        }  

    }  

}

public class Setup

{  

    public static void main(String[] args) {  

        BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(2);  

        //不设置的话,LinkedBlockingQueue默认大小为Integer.MAX_VALUE  

        Consumer consumer = new Consumer(queue);  

        Producer producer = new Producer(queue);  

        for (int i = 0; i < 5; i++) {  

           new Thread(producer, "Producer" + (i + 1)).start();  

           new Thread(consumer, "Consumer" + (i + 1)).start();  

        }  

    }  

}  

原创粉丝点击