Java多线程学习之 BlockingQueue实现生产者和消费者模型

生产者和消费者模型是操作系统中经典的同步问题。其设计要求为:

(1)生产者和消费者共同使用一个缓冲区;

(2)生产者生产资源,使得缓冲区的资源增加,但当缓冲区存放的资源达到最大时,生产者生产的线程会被堵塞,直到消费者消耗了缓冲区的资源后,线程才被重新唤醒。

(3)消费者消耗资源,使得缓冲区的资源减少,但当缓冲区存放的资源为空时,消费者的线程会被堵塞,直到生产者为缓冲区生产了新的资源后,线程才被重新唤醒。

在Java5之前,一般是通过线程中的wait和notify方法来实现线程的调度的。wait()方法将线程置入“睡眠”状态,同时又“积极”地等待条件发生改变.而且只有在一个notify()或notifyAll()发生变化的时候,线程才会被唤醒,并检查条件是否有变。

基于wait和notify方法实现生产者和消费者模型的代码如下:

package com.bos.test.concurrent.producer_consumer;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 生产者-消费者   基于wait和notify的实现
 * @author betty
 *
 */

class Constants{
	// 最大任务数量
	public static final Integer MAX_TASK_NUM = 5;
	// 生产者数量
	public static final Integer PRODUCER_NUM = 2;
	// 消费者数量
	public static final Integer CONSUMER_NUM = 3;
}

/**
 * 工作任务
 * */
class Task{
	
	private String id;
	
	public Task() {
		id = UUID.randomUUID().toString();
	}
	
	@Override
	public String toString() {
		return "Task [id=" + id + "]";
	}
}

/**
 * 生产者
 * */
class Producer implements Runnable{

	private List buffer;

	public Producer(List buffer) {
		this.buffer = buffer;
	}
	
	@Override
	public void run() {
		while(true) {
			synchronized (buffer) {
				// 判断当前任务列表中的任务数量是否大于设点的最大任务数量
				while(buffer.size()>=Constants.MAX_TASK_NUM) {
					try {
						System.out.println("tasks are full!!!");
						buffer.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				// 创建新的任务
				Task task = new Task();
				buffer.add(task);
				buffer.notifyAll();
				try {
					Thread.sleep(1000L*2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("Producer[" + Thread.currentThread().getName() + "] put " + task);
			}
		}
	}
}

/**
 * 消费者
 * */
class Consumer implements Runnable{

	private List buffer;
	
	public Consumer(List buffer) {
		this.buffer = buffer;
	}
	
	@Override
	public void run() {
		while(true) {
			synchronized (buffer) {
				// 判断当前任务列表中的任务数量是否为零
				while(buffer.isEmpty()) {
					try {
						System.out.println("tasks are empty!!!");
						buffer.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				// 消耗当前一个任务
				Task task = buffer.remove(0);
				buffer.notifyAll();
				try {
					Thread.sleep(1000L*2);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("Consumer[" + Thread.currentThread().getName() + "] got " + task);
			}
		}
	}
	
}

public class Wait_Notify {

	public static void main(String[] args) {
		// 设置列表的最大任务数量
		List buffer = new ArrayList(Constants.MAX_TASK_NUM);
		// 设置线程池
		ExecutorService service = Executors.newFixedThreadPool(Constants.CONSUMER_NUM+Constants.PRODUCER_NUM);
		for(int i=0;i
前面写了那么多,终于轮到主角登场了

阻塞队列--BlockingQueue,该队列相当于生产者和消费者模型中的缓冲区:

(1)当队列为空时,即缓冲区存放的资源为空,消费者的线程会被堵塞。直到有数据放入队列,线程会自动唤醒。

(2)当队列中填满数据时,即缓冲区存放的资源达到最大,生产者的线程会被堵塞,直到队列有空间时,线程会自动唤醒。

其插入元素的方法有:
offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,
            则返回true,否则返回false.(本方法不阻塞当前执行方法的线程)
offer(E o, long timeout, TimeUnit unit),可以设定等待的时间,如果在指定的时间内,还不能往队列中
            加入BlockingQueue,则返回失败。
put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断
            直到BlockingQueue里面有空间再继续。

取出元素的方法有:
poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,
            取不到时返回null;
poll(long timeout, TimeUnit unit):取出一个队首的对象,如果在指定时间内,队列一旦有数据
            可取,则立即返回队列中的数据。否则直到时间超时还没有数据可取,返回null。
take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到
            BlockingQueue有新的数据被加入; 
drainTo():一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数), 
            通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。

BlockingQueue常用成员介绍:
(1)ArrayBlockingQueue。基于数组的阻塞队列实现,有界,在ArrayBlockingQueue内部,维护了一个定长数组。
(2)LinkedBlockingQueue。基于链表的阻塞队列,无界,其大小可以任意设置。

基于BlockingQueue实现生产者和消费者模型的代码如下:

package com.bos.test.concurrent.producer_consumer;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 生产者-消费者   基于BlockingQueue的实现
 *   在调用put方法向队列中插入元素时,如果队列已满,它会让插入元素的线程等待队列腾出空间;
 *   在调用take方法从队列中取元素时,如果队列为空,取出元素的线程就会阻塞。
 * @author betty
 *
 */

class Common{
	// 最大任务数量
	public static final Integer MAX_TASK_NUM = 5;
	// 生产者数量
	public static final Integer PRODUCER_NUM = 4;
	// 消费者数量
	public static final Integer CONSUMER_NUM = 2;
}

/**
 * 工作任务
 * */
class TaskBlocking{
	
	private String id;
	
	public TaskBlocking() {
		id = UUID.randomUUID().toString();
	}
	
	@Override
	public String toString() {
		return "TaskBlocking [id=" + id + "]";
	}
}

class ProducerBlocking implements Runnable{
	
	private BlockingQueue buffer;
	
    public ProducerBlocking(BlockingQueue buffer) {
		this.buffer = buffer;
	}
    
	@Override
	public void run() {
		while(true) {
			try {
				
				TaskBlocking task = new TaskBlocking();
				buffer.put(task);
				Thread.sleep(1000L*2);
				System.out.println("Producer[" + Thread.currentThread().getName() + "] put " + task);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

class ConsumerBlocking implements Runnable {

	private BlockingQueue buffer;

	public ConsumerBlocking(BlockingQueue buffer) {
		this.buffer = buffer;
	}

	@Override
	public void run() {
		while(true) {
			try {
				TaskBlocking task = buffer.take();
				Thread.sleep(1000L*2);
				System.out.println("Consumer[" + Thread.currentThread().getName() + "] got " + task);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

public class BlockingQueueTest {
	
	public static void main(String[] args) {
		BlockingQueue buffer = new LinkedBlockingQueue(Common.MAX_TASK_NUM);
		ExecutorService service = Executors.newFixedThreadPool(Common.CONSUMER_NUM+Common.PRODUCER_NUM);
		for(int i=0;i


参考网址:

http://blog.csdn.net/shjniu3000/article/details/53487040

http://blog.csdn.net/jackfrued/article/details/44499227

你可能感兴趣的:(Java-多线程并发,Java多线程,BlockingQueue,生产者和消费者模型)