阻塞队列
-
当阻塞队列是空时,从队列获取元素的操作将会被阻塞
-
当阻塞队列是满时,往队列里添加元素的操作将会被阻塞
阻塞队列的优点
我们不需要关心什么时候阻塞线程,什么时候需要唤醒线程
在concurrent包发布以前,在多线程环境下,我们每个程序员都必须自己取控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。
BlockingQueue的核心方法
-
异常:
NoSuchException
-
特殊值: true、false
-
阻塞: 一直等待直到put成功,或者响应中断退出
-
超时: 阻塞一定时间,超时后退出
阻塞队列的种类
BlockingQueue阻塞队列是属于一个接口,底下有七个实现类
- ArrayBlockingQueue:由数组结构组成的有界阻塞队列
-
LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为Integer.MAX_VALUE)阻塞队列,但是界限非常大,相当于无界,可以当成无界
-
PriorityBlockingQueue:支持优先级排序的无界阻塞队列
-
DelayQueue:使用优先级队列实现的延迟无界阻塞队列
-
SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列
SynchronousQueue没有容量,与其他BlockingQueue不同,SynchronousQueue是一个不存储的BlockingQueue,每一个put操作必须等待一个take操作,否者不能继续添加元素
-
LinkedTransferQueue:由链表结构组成的无界阻塞队列
-
LinkedBlokingDeque:由链表结构组成的双向阻塞队列
阻塞队列用途
- 生产者消费者
-
传统写法
/** * 题目:一个初始值为0的变量,两个线程对其交替操作,一个加1一个减1 * 1 线程 操作 资源类 * 2 判断 干活 通知 * 防止虚假唤醒 */ public class ProdConsumer_TraditionDemo { public static void main(String[] args){ ShareData shareData = new ShareData(); //A:++ new Thread(() ->{ for(int i = 0; i < 5; i++){ try { shareData.increment(); }catch (Exception e){ e.getStackTrace(); } } },"A").start(); //B-- new Thread(() ->{ for(int i = 0; i < 5; i++){ try { shareData.dcrement(); }catch (Exception e){ e.getStackTrace(); } } },"B").start(); } } class ShareData{ private int number = 0; private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void increment()throws Exception { //加锁 lock.lock(); try { //1 判断 while (number != 0) { //等待,不能生产 为了防止出现虚假唤醒机制,不能使用if来进行判断,而应该使用while condition.await(); } //干活 number++; System.out.println(Thread.currentThread().getName() + "\t" + number); //通知,唤醒 condition.signalAll(); } catch (Exception e) { e.getStackTrace(); } finally { //解锁 lock.unlock(); }} public void dcrement ()throws Exception { lock.lock(); try { //1 判断 while (number == 0) { //等待,不能生产 condition.await(); } number--; System.out.println(Thread.currentThread().getName() + "\t" + number); condition.signalAll(); } catch (Exception e) { e.getStackTrace(); } finally { lock.unlock(); } } }
-
输出:
-
阻塞队列版
class MyResource{ //默认开启,进行生产和消费 //用volatile保证可见性 private volatile boolean FLAG = true; //用原子类代替number++ private AtomicInteger atomicInteger = new AtomicInteger(); BlockingQueue<String> blockingQueue = null; //通过构造函数确定class public MyResource(BlockingQueue<String> blockingQueue) { this.blockingQueue = blockingQueue; System.out.println(blockingQueue.getClass().getName()); } //生产 public void myProd()throws Exception{ String data = null; boolean retValue = false; while (FLAG){ //以原子方式将当前值加 1 data = atomicInteger.incrementAndGet()+""; //每2s插入一个数据进入队列中 // retValue = blockingQueue.offer(data,2L, TimeUnit.SECONDS); if(retValue){ System.out.println(Thread.currentThread().getName()+"\t插入队列成功,数据为:"+data); }else{ System.out.println(Thread.currentThread().getName()+"\t插入队列失败"); } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("PRO:flag = false ,stop"); } //消费者 public void myConsumer()throws Exception{ String result = null; while (FLAG){ //每2s取出一个数据 result = blockingQueue.poll(2L,TimeUnit.SECONDS); //等待2s,取出result为空,返回null if(result == null && result.equalsIgnoreCase("")){ FLAG = false; System.out.println(Thread.currentThread().getName() + "\t 消费失败,队列中已为空,退出" ); return; } System.out.println(Thread.currentThread().getName()+"\t 消费成功,取出数据为: "+result); } } //定义一个手动停止生产和消费的方法 public void stop(){ this.FLAG = false; } } /** * volatile/CAS/atomicInteger/BlockQueue/线程交互/原子引用 */ public class ProdConsumer_BlockQueueDemo { public static void main(String[] args){ // 传入具体的实现类, ArrayBlockingQueue MyResource myResource = new MyResource(new ArrayBlockingQueue<>(10)); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"\t 生产线程启动"); try { myResource.myProd(); System.out.println(); System.out.println(); }catch (Exception e){ e.getStackTrace(); } },"Prod").start(); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"\t 消费线程启动"); try { myResource.myConsumer(); }catch (Exception e){ e.getStackTrace(); } },"Consumer").start(); //5s后手动停止生产消费 try{ TimeUnit.SECONDS.sleep(5); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(); System.out.println(); System.out.println("老板:stop"); myResource.stop(); } }
- 输出:
- 输出:
-
线程池
-
消息中间件