阻塞队列BlockingQueue扩展了Queue、Collection接口,对元素的插入和提取使用了“阻塞”处理,我们知道Collection下的实现类一般都采用了长度自行管理方式(也就是自动变长),比如下面的代码是可以运行的:
集合的自动变长
上面的代码定义了列表的初始长度为5,在实际使用时,当加入的元素超过初始容量时,ArrayList会自行扩容,确保能够正常加入元素。那BlockingQueue也是集合,也实现了Collection接口,它的容量是否会自行管理呢?我们来试下:
BlockingQueue的不可变长
从结果可以看到,BlockingQueue是不会自动管理的,会报队列已满异常,这是阻塞队列和非阻塞队列的一个重要区别:阻塞队列的容量时固定的,非阻塞队列则是变长的。阻塞队列可以在声明时指定队列的容量,若指定指定的容量,则元素的数量不可超过该容量,若不指定,队列的容量为Integer的最大值。
阻塞队列和非阻塞队列有此区别的原因是阻塞队列是为了容纳(或排序)多线程任务而存在的,其服务的对象是多线程应用,而非阻塞队列是为了容纳(或排序)多线程任务而存在的,其服务的对象是多线程应用,而非阻塞队列容纳的则是普通的数据元素。
下面看下BlockingQueue的add源码。
add方法
offer方法
其他源码有兴趣的可以去看看。上面再加入元素时,如果判断出当前队列已满,则返回false,表示插入失败,之后再包装成队列满异常。那么这边为什么不直接调用offer呢?因为我们如果直接调用offer方法插入元素,在超出容量的情况下,它除了返回false外,不会提供任何信息,如果我们的代码不做插入判断的话,那么我们数据插入就会丢失找不到原因。这就是它与非阻塞队列的不同之处。
阻塞队列的这种机制对异步运算是非常有帮助的,例如我们定义深度为100的阻塞队列容纳100个任务,多个线程从该队列中获取任务并处理,当所有的线程都在繁忙,并且任务中的数量已经为100时,也预示着系统运算压力非常巨大,而且处理结果的时间也会比较长,于是在第101个任务期望加入时,队列拒绝加入,而且返回异常,由系统自动处理。但是如果应用期望无论等待多长时间都要运行该任务,不希望返回异常,那么该怎么处理好呢?
此时就要用到BlockingQueue接口定义的put方法了,它的作用也是把元素加入到队列里面,但它和add、offer方法不同,它会等待队列空出元素,再让自己加入进去。也就是说,put方法提供的是一种“无赖”式的插入,无论等待多长时间都要把钙元素插入到队列中。下面看下它的源码。
put源码
可以看到这边队列满的时候,等待其他线程元素元素(count==items.length)。如果被中断了,唤醒其他的线程。put方法的目的就是去报元素肯定会加入到队列中,但是这种方法有个坏处就是此种等待是一个循环,会不停的消耗系统资源,当等待加入的元素数量较多时势必会对系统性能产生影响。
那么这个要怎么解决呢?在这个方法里还提供了带有超时时间的offer方法,其实现的方法与put类似,只是使用了Condition的awaitNanos方法来判断当前线程已经等待了多少时间,超时则返回false。
总结起来就是一句话吧,阻塞队列的长度是固定的。
好了今天就到这里,谢谢大家的收看。
喜欢的关注一波~~~
