BlockingQueue介绍
BlockingQueue是java.util.concurrent包下的一个接口
-
它的继承关系是:它是继承Queue的一个接口
Collection
|—Queue
||—BlockingQueue -
它的主要意义:在于应用在两个以上的线程同时对队列进行入队/出队的任务的情况下,显然由于多线程执行过程中的随机性,出现一个线程一直在从队列出列获取元素而队列却来不及入队新元素而没办法按要求获取到要求的元素了。因此,我们引入了阻塞队列,它需要我们指定队列长度来限制入队操作,当队列满时能够进行阻塞等待另一个线程对该队列进行出队操作!同理,当出队的时候若队内无元素也能够在一定时间内阻塞出队线程等待其他线程增添元素!
这就好像一个生产者——消费者模型,生产者生产数量足够多的时候会进行阻塞等待消费者,消费者得不到商品则会阻塞等待生产者生成新商品!
核心API方法
public interface BlockingQueue<E> extends Queue<E> {
/*无阻塞*/
boolean add(E e);//将指定元素插入此队列中(如果立即可行且不会违反容量限制),
//成功时返回 true,如果当前没有可用的空间,则抛出 IllegalStateException。
boolean offer(E e) //将指定元素插入此队列中(如果立即可行且不会违反容量限制),
//成功时返回 true,如果当前没有可用的空间,则返回 false。(一次offer定成败)
/*可以一直阻塞*/
void put(E e) throws InterruptedException;//将元素设置到队列中,
//如果队列中没有多余的空间,该方法会一直阻塞,直到队列中有多余的空间。
E take() throws InterruptedException;//从队列中获取值,如果队列中没有值,
//线程会一直阻塞,直到队列中有值,并且该方法取得了该值。
/*定时阻塞*/
//将给定元素在给定的时间内设置到队列中,若队列中空间已满,则线程会阻塞至时间结束直至返回false
boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException;
//在给定的时间里,从队列中出列,时间到了直接调用普通的poll方法,为null则直接返回null。
E poll(long timeout, TimeUnit unit)
throws InterruptedException;
/*一般方法*/
//获取队列中剩余的空间。
int remainingCapacity();
//将队列中值,全部移除,并发设置到给定的集合中。
int drainTo(Collection<? super E> c);
//指定最多数量限制将队列中值,全部移除,并发设置到给定的集合中。
int drainTo(Collection<? super E> c, int maxElements);
//从此队列中移除指定元素的单个实例(如果存在)。
boolean remove(Object o)
//如果此队列包含指定元素,则返回 true。
boolean contains(Object o)
常用的实现类
-
ArrayBlockingQueue
规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小。其所包含的对象是以FIFO(先入先出)顺序排序的。 -
LinkedBlockingQueue
大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定。其所包含的对象是以FIFO(先入先出)顺序排序的。 -
PriorityBlockingQueue
类似于LinkedBlockingQueue,但其所含对象的顺序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序。 -
SynchronousQueue 特殊的BlockingQueue,对其的操作必须是放和取交替完成的
。
offer、poll方法的阻塞机制测试
4. public class TestBlockingQueue {
5. /**
6. * 测试入队方法
7. */
8. @Test
9. public void testOffer() {
10. BlockingQueue<Integer> queue
11. = new ArrayBlockingQueue<Integer>(10);
12. for(int i=0;i<20;i++){
13. try {
14. //设置5秒超时,5秒内元素仍没有入队到队列中,则返回false
15. boolean b = queue.offer(i,5,TimeUnit.SECONDS);
16. System.out.println("存入是否成功:"+b);
17. } catch (InterruptedException e) {
18. e.printStackTrace();
19. }
20. }
21. }
22. }
输出如下结果:显然在满队列的情况下进入阻塞状态等待消费者但由于本例无消费者因此后10次添加元素失败了!
- 存入是否成功:true
- 存入是否成功:true
- 存入是否成功:true
- 存入是否成功:true
- 存入是否成功:true
- 存入是否成功:true
- 存入是否成功:true
- 存入是否成功:true
- 存入是否成功:true
- 存入是否成功:true
- 存入是否成功:false
- 存入是否成功:false
- 存入是否成功:false
- 存入是否成功:false
- 存入是否成功:false
- 存入是否成功:false
- 存入是否成功:false
- 存入是否成功:false
- 存入是否成功:false
- 存入是否成功:false
4. public class TestBlockingQueue {
5. /**
6. * 测试出队方法
7. */
8. @Test
9. public void testPull() {
10. BlockingQueue<Integer> queue
11. = new ArrayBlockingQueue<Integer>(10);
12. for(int i=0;i<10;i++){
13. queue.offer(i);
14. }
15. for(int i =0;i<20;i++){
16. //获取元素,设置5秒超时,5秒内还没有元素可取出则返回null
17. try {
18. Integer num = queue.poll(5, TimeUnit.SECONDS);
19. System.out.println("元素:"+num);
20. } catch (InterruptedException e) {
21. e.printStackTrace();
22. }
23. }
24. }
25. }
同理介于poll方法的阻塞机制也会得到如下的结果
- 元素:0
- 元素:1
- 元素:2
- 元素:3
- 元素:4
- 元素:5
- 元素:6
- 元素:7
- 元素:8
- 元素:9
- 元素:null
- 元素:null
- 元素:null
- 元素:null
- 元素:null
- 元素:null
- 元素:null
- 元素:null
- 元素:null
- 元素:null
生产者——消费者模型模拟
public class ArrayBlockingQueueDemo {
public static void main(String[] args) {
BlockingQueue<Integer> que = new ArrayBlockingQueue<>(10);
Thread th = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0 ; i < 60 ; i ++) {
try {
System.out.println("是否传入"+i+"成功?:" + que.offer(i,10,TimeUnit.SECONDS));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread th2 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0 ; i < 20 ; i ++)
{
try {
synchronized (que) {
System.out.println(Thread.currentThread().getName()+": "+que.poll(1, TimeUnit.SECONDS));
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
Thread th3 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0 ; i < 20 ; i ++)
{
try {
synchronized (que) {
System.out.println(Thread.currentThread().getName()+": "+que.poll(1, TimeUnit.SECONDS));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread th4 = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0 ; i < 20 ; i ++)
{
try {
synchronized (que) {
System.out.println(Thread.currentThread().getName()+": "+que.poll(1, TimeUnit.SECONDS));
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
th.start();
th2.start();
th3.start();
th4.start();
}
}
结果如下
Thread-1: 0
是否传入0成功?:true
是否传入1成功?:true
Thread-1: 1
是否传入2成功?:true
Thread-1: 2
是否传入3成功?:true
Thread-1: 3
是否传入4成功?:true
Thread-1: 4
是否传入5成功?:true
Thread-1: 5
是否传入6成功?:true
Thread-1: 6
是否传入7成功?:true
Thread-1: 7
是否传入8成功?:true
Thread-1: 8
是否传入9成功?:true
Thread-1: 9
是否传入10成功?:true
Thread-1: 10
是否传入11成功?:true
是否传入12成功?:true
是否传入13成功?:true
Thread-1: 11
是否传入14成功?:true
是否传入15成功?:true
是否传入16成功?:true
是否传入17成功?:true
是否传入18成功?:true
是否传入19成功?:true
是否传入20成功?:true
是否传入21成功?:true
是否传入22成功?:true
Thread-1: 12
Thread-1: 13
Thread-1: 14
是否传入23成功?:true
是否传入24成功?:true
是否传入25成功?:true
Thread-1: 15
Thread-1: 16
Thread-1: 17
Thread-1: 18
Thread-1: 19
Thread-3: 20
Thread-3: 21
Thread-3: 22
Thread-3: 23
Thread-3: 24
是否传入26成功?:true
Thread-2: 25
是否传入27成功?:true
Thread-2: 26
是否传入28成功?:true
Thread-2: 27
是否传入29成功?:true
是否传入30成功?:true
是否传入31成功?:true
是否传入32成功?:true
是否传入33成功?:true
是否传入34成功?:true
是否传入35成功?:true
是否传入36成功?:true
是否传入37成功?:true
是否传入38成功?:true
Thread-2: 28
Thread-2: 29
Thread-2: 30
Thread-2: 31
Thread-2: 32
Thread-2: 33
Thread-2: 34
Thread-2: 35
Thread-2: 36
Thread-2: 37
Thread-2: 38
Thread-2: 39
是否传入39成功?:true
是否传入40成功?:true
是否传入41成功?:true
是否传入42成功?:true
是否传入43成功?:true
是否传入44成功?:true
是否传入45成功?:true
是否传入46成功?:true
是否传入47成功?:true
Thread-2: 40
Thread-2: 41
Thread-2: 42
Thread-2: 43
是否传入48成功?:true
是否传入49成功?:true
Thread-2: 44
是否传入50成功?:true
是否传入51成功?:true
是否传入52成功?:true
是否传入53成功?:true
是否传入54成功?:true
Thread-3: 45
Thread-3: 46
Thread-3: 47
Thread-3: 48
Thread-3: 49
Thread-3: 50
Thread-3: 51
Thread-3: 52
Thread-3: 53
是否传入55成功?:true
Thread-3: 54
是否传入56成功?:true
Thread-3: 55
是否传入57成功?:true
Thread-3: 56
是否传入58成功?:true
Thread-3: 57
是否传入59成功?:true
Thread-3: 58
Thread-3: 59
我们观察能够发现,通过定义的限定空间为10的ArrayBlockingQueue,我们完成了60个元素的生产、消费调度,且过程中不会出现生产失败或消费失败的情况(利用了阻塞等待机制)