lvhaosir
一个正在奋起的菜鸟...

导航

 

阻塞队列

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

  1. 支持阻塞的插入方法:当队满时,队列会阻塞插入元素的线程,一直到队列不满。
  2. 支持阻塞的移出方法:当队列为空时,获取元素的线程会等待队列变为非空。

阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列就是生产者用来存放元素、消费者用来获取元素的容器。

部分方法

方法 抛出异常 返回特殊值 一直阻塞 超时退出
插入方法 add(e) offer(e) put(e) offer(e,time,unit)
移出方法 remove() poll() take() poll(time,unit)
检查方法 element() peek() 无 无
  • 抛出异常:是指当阻塞队列满时,再往队列里插入元素,会抛出java.lang.IllegalStateException: Queue full 。当队列为空时,从队列里获取元素时,会抛出NoSuchElementException。
  • 返回特殊值:插入成功会返回true,失败为false。移除方法:队列有值就返回值,队列空就返回null。
  • 一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程。当队列为空时,消费者线程试图从队列里take元素,队列也会阻塞消费者线程,直到队列可用。
  • 超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。
package cn.lvhaosir;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class Test006 {
	public static void main(String[] args) {
		
		ArrayBlockingQueue<String> abq = new ArrayBlockingQueue<String>(4);
		/**
		 *  四种插入方式
		 */
		// 若队满,抛出异常,java.lang.IllegalStateException: Queue full
		boolean add = abq.add("张三");
		System.out.println(add);
		// 若队满,返回值为:false
		boolean offer = abq.offer("李四");
		try {
			// 若队满,则线程一直阻塞,直到数据插入
			abq.put("王五");
			// 若队满,线程会阻塞2秒,也可以获取一个返回值
			boolean offer2 = abq.offer("老六", 2, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 若队空,抛出异常NoSuchElementException
		String remove = abq.remove();
		// 若队空,返回null
		String poll = abq.poll();
		try {
			// 若队空,一直阻塞
			String take = abq.take();
			// 若队空,线程会阻塞2秒,获取null或值
			String poll2 = abq.poll(2, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

常用的阻塞队列

ArrayBlockingQueue
  • ArrayBlockingQueue是一个有边界的阻塞队列,它的内部实现是一个数组。有边界的意思是它的容量是有限的。我们必须在其初始化的时候执行它的容量大小,容量大小一旦指定就无法改变。ArrayBlockingQueue是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。
LinkedBlockingQueue
  • LinkedBlockingQueue阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,它就是有边界的。如果不指定,它就是无边界的,其实是采用了默认大小为Integer.MAX_VALUE的容量。内部实现是一个链表。和ArrayBlockingQueue一样,LinkedBlockingQueue也是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。
PriorityBlockingQueue
  • PriorityBlockingQueue是一个没有边界的队列,它的排序规则和 java.util.PriorityQueue一样,PriorityBlockingQueue中允许插入null对象。所有插入PriorityBlockingQueue的对象必须实现 java.lang.Comparable接口,队列优先级的排序规则就是按照我们对这个接口的实现来定义的。另外,我们可以从PriorityBlockingQueue获得一个迭代器Iterator,但这个迭代器并不保证按照优先级顺序进行迭代。
SynchronousQueue
  • SynchronousQueue队列内部仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞,除非这个元素被另一个线程消费。可以认为SynchronousQueue是一个缓存值为1的阻塞队列,但是 isEmpty()方法永远返回是true,remainingCapacity() 方法永远返回是0,remove()和removeAll() 方法永远返回是false,iterator()方法永远返回空,peek()方法永远返回null,size()永远返回0。

常用的非阻塞队列

ConcurrentLinkedDeque
  • ConcurrentLinkedDeque是一个适用于高并发场景下的队列,通过无锁的复方石,实现了高并发状态下的高性能,通常ConcurrentLinkedQueue性能好于BlockingQueue,它是一个基于链接节点的无界线程安全队列,该队列的元素遵循先进先出的原则。最新插入的对象是尾部,最新移出的对象是头部,该队列不允许null元素。

使用阻塞队列,模拟生产者消费者

package cn.lvhaosir;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class ProducerThread extends Thread {
	
	// 阻塞队列
	private BlockingQueue<String> blockingQueue;
	// 标志
	private volatile boolean flag = true;
	// 原子数
	private static AtomicInteger count = new AtomicInteger();
	public ProducerThread(BlockingQueue<String> blockingQueue) {
		super();
		this.blockingQueue = blockingQueue;
	}
	@Override
	public void run() {
		System.out.println("生产者线程启动...");
		try {
			// 如果 flag 为 true,代表为空,就生产
			while(flag) {
				String data = String.valueOf(count.getAndIncrement());
				// 生产
				boolean offer = blockingQueue.offer(data, 2, TimeUnit.SECONDS);
				if (offer) {
					System.out.println("生产成功,data="+data);
				} else {
					System.out.println("生产失败...");
				}
				// 停顿1s
				Thread.sleep(1000);
			}
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			System.out.println("生产者离职...");
		}
	}
	
	public void stopThread() {
		this.flag = false;
	}
	
}
class ConsumerThread extends Thread {
	
	private BlockingQueue blockingQueue;
	
	private volatile boolean flag = true ;

	public ConsumerThread(BlockingQueue blockingQueue) {
		super();
		this.blockingQueue = blockingQueue;
	}
	@Override
	public void run() {
		try {
			
			while(flag) {
				// 如果有资源,就去消费
				String data = (String) blockingQueue.poll(2, TimeUnit.SECONDS);
				if(data != null) {
					System.out.println("消费成功,得到数据 data="+data);
				} else {
					System.out.println("消费失败,没有资源");
					this.flag = false;
				}
				// 停顿1s
//				Thread.sleep(1000);
			}
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			System.out.println("消费者走了...");
		}
	}
}
public class Test007 {
	public static void main(String[] args) throws InterruptedException {
		BlockingQueue blockingQueue = new LinkedBlockingQueue();
		ProducerThread p1 = new ProducerThread(blockingQueue);
		ProducerThread p2 = new ProducerThread(blockingQueue);
		ConsumerThread c1 = new ConsumerThread(blockingQueue);
		p1.start();
		p2.start();
		c1.start();
		// 运行 3s
		Thread.sleep(3000);
		// 结束生产
		p1.stopThread();
		p2.stopThread();
	}
}

转载请附上原文链接,有问题请留言,谢谢支持。

posted on 2018-08-01 19:43  lvhaosir6  阅读(523)  评论(0)    收藏  举报