上面的Account的取款、存款问题,抽象一下:一个Account,两个任务(一个存款、一个取款),每个任务两条线程(但两条线程完成的并不是同一项任务)
BlockingQueue是一个阻塞队列接口,它有很多实现类,见下图:来源于《Java疯狂讲义 第三版》
实现类:
ArrayBlockingQueue:基于数组实现
LinkedBlockingQueue:基于链表实现
PriorityBlockingQueue:内部元素按照排序器排序,并非先进先出
SynchronousQueue:同步队列,存取交替进行
DelayQueue:内部元素实现Delay接口,内部元素按照getDelay()的返回值排序
该接口是Queue的子接口,但并不是作为容器使用,而是作为线程同步工具使用。
当一个线程要往里面put()一个元素时,若队列已满,则线程阻塞
当一个线程从里面take()一个元素时,若队列为空,则线程阻塞
三类方法
在队列尾部插入元素:若队列已满,分别会:
add(E e):抛出异常
offer(E e):返回false
put(E e):阻塞队列
在队列头部取出元素,并删除元素:若队列为空,分别会:
remove():抛出异常
poll():返回false
take():阻塞队列
在队列头部取出元素,但不删除元素:若队列为空,分别会:
element():抛出异常
peek():返回false
见示例:
package testpack; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class Test2 { public static void main(String[] args){ BlockingQueue<String> bq=new ArrayBlockingQueue<>(1); new Producer(bq,"生产者A").start(); new Producer(bq,"生产者B").start(); new Consumer(bq,"消费者X").start(); //两个生产者,一个消费者,会产生阻塞 } } class Producer extends Thread{ private BlockingQueue<String> bq; Producer(BlockingQueue bq,String name){ super(name); this.bq=bq; } public void run(){ //run()方法没有被同步,for循环中的代码可能被分开执行 String[] str={"A","B","C"}; for (int i=0;i<3;i++){ System.out.println(getName()+" 准备向阻塞队列中添加元素"); try{ bq.put(str[i%3]); }catch(InterruptedException ex){ ex.printStackTrace(); } System.out.println(getName()+"添加元素完成: "+bq); } } } class Consumer extends Thread{ private BlockingQueue<String> bq; Consumer(BlockingQueue bq,String name){ super(name); this.bq=bq; } public void run(){ for (int i=0;i<3;i++){ System.out.println(getName()+" 准备从阻塞队列中取出元素"); try{ System.out.println(getName()+"取出元素成功: "+bq.take()); }catch(InterruptedException ex){ ex.printStackTrace(); } } } }输出结果如下:
生产者A 准备向阻塞队列中添加元素 //线程A被中断,可能在添加成功前或后
生产者B 准备向阻塞队列中添加元素
//线程B可能被中断,可能被阻塞
生产者A添加元素完成: [M]
//线程A添加成功
生产者A 准备向阻塞队列中添加元素
//线程A阻塞
消费者X 准备从阻塞队列中取出元素
消费者X取出元素成功: M
//线程X取出成功
消费者X 准备从阻塞队列中取出元素
//线程X被阻塞
生产者B添加元素完成: [M]
//线程B添加成功
生产者A添加元素完成: [N]
//这里之所以连续添加2次,因为X已将元素取出,但没有输出
消费者X取出元素成功: M
//X将取出的元素输出
生产者A 准备向阻塞队列中添加元素
//线程A被阻塞或中断
生产者B 准备向阻塞队列中添加元素
//线程B被阻塞或中断
消费者X 准备从阻塞队列中取出元素
消费者X取出元素成功: N
//X将取出的元素输出
生产者A添加元素完成: [K]
//三次消费已执行结束,生产者线程还在执行,程序阻塞
ArrayBlockingQueue内部定义了一把private的ReentrantLock锁,在创建对象时创建锁对象(false策略)
在put()/take()阻塞的时候,会释放ReentrantLock锁对象