前面Java并发这块的锁、并发工具、原子类、线程池都已经学习完了,接下来是队列的学习。
ArrayBlockingQueue总结
先直接总结ArrayBlockingQueue相关的特性再根据源码来进行说明,它的主要特性如下:
1、他是一个由数组实现的FIFO有界阻塞队列,数组由final修饰;
2、ArrayBlockingQueue有界且固定,在构造函数时必须指定大小,确认后不支持改变(确定数组且不可变);
3、在多线程环境下不保证“公平性”;
4、通过ReentrantLock与Condition实现线程安全;
重要属性介绍
主要属性如下图:
ArrayBlockingQueue的属性还是比较简单,首先是存放数据的Object数组,它是final修饰的,所以队列的长度不可变。
然后三个int属性分别表示下次获取数据时应该从数组的哪里获取,下次保存数据时应该保存到数组的哪里,count记录着还有多少个可以拿。
所有方法首先都必须获取到lock的锁,lock有公平与非公平锁,默认实现的是非公平锁,也可以在初始化ArrayBlockingQueue指定,所以默认ArrayBlockingQueue并不保证公平性。
notEmpty与notFull都是通过lock创建,都是在初始化ArrayBlockingQueue是初始化出来。
这里简单介绍了属性的作用,接下来会通过源码再来理解它的作用。
最关键的两个私有方法
首先要说两个私有方法,应该队列主要的方法最后都依赖这两个私有方法,直接看源码如下图:
enqueue方法用来把数据保存到数组items中,会递增putIndex,也就是下次应该保存的位置,如果putIndex等于了数组的长度,则下次为0。
最后会唤醒那些调用notEmpty.await()阻塞的线程,实际上只有take方法调用了。
dequeue方法是获取数据,获取的是takeIndex处的数据,在获取过后会把items[takeIndex]处设置为null,同样递增takeIndex后如果等于了数组的长度则会被置为0。
最后会唤醒那些调用notFull.await()阻塞的线程,只有put方法调用了。
所以主要的变化在于putIndex和takeIndex,这里总结了他们有如下4种关系:
如果不加保护他们不止这些关系,比如takeIndex比putIndex跑的快,那么就会获取到null值,获取putIndex比takeIndex多走一个轮回还多那么就会出现数据被覆盖,造成数据丢失。
只有保证它们是这几种关系才能保证数据的安全性,避免数据被覆盖或者获取到null值,并且实现了FIFO先进先出,而队列提供的公共方法都必须要保证这种安全。
添加数据方法介绍
ArrayBlockingQueue提供了4个添加方法add、offer、offer(指定阻塞时间)、put;
add方法调用的是offer方法,而offer方法先获取到锁,再判断队列是否已满,已满直接返回false,没有满则调用enqueue保存数据。所以add与offer没有阻塞。
offer还有一个可以在队列已满情况下阻塞指定时间(timeout)在尝试保存的方法,在判断队列已满的情况会调用”nanos = notFull.awaitNanos(nanos);”阻塞线程,一定时间后如果还是满的则会返回false。
put获取到锁,如果数组已满则调用notFull.await一直阻塞等待唤醒,唤醒后再次验证是否已满,没满则调用enqueue,否则再次阻塞。所以向队列中加数据只有put方法才支持真正的阻塞,保证添加成功,其他方法会可能保存失败。
获取数据方法介绍
获取数据提供了poll、poll(指定阻塞时间)、take、peek
poll方法先获取到锁,如果count==0则返回null,否则调用dequeue方法获取结果。
poll(指定阻塞时间)在判断count==0时会先阻塞执行时间,然后再次判断,如果还是等于0则返回null,如果不等于0则调用dequeue方法获取结果。
take方法先获取到锁然后在循环判断count是否等于0如果等于0则调用notEmpty.await()阻塞,否则调用dequeue获取结果,同样take方法也会保证一定能拿到数据,否则会一直阻塞。
peek是偷看的意思,peek方法在获取到锁后直接获取item[takeIndex]的元素返回,然后不做任何事情,就好像偷偷看下下一次会获取哪一个元素,但是不影响队列。
总结
ArrayBlockingQueue还有一些其他次要的方法,就不再一一说明了,ArrayBlockingQueue通过两个索引来保存和拉取数据,并且实现了先进先出的特性。虽然数组的长度是固定有限的,但是通过循环使用数组,也能通过他同步无限的数据。
分析方法发现只有take方法和put方法才能保证真正获取或者保存数据成功,如果没有成功则会一直阻塞,其他方法则不一定。
Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!
来源:oschina
链接:https://my.oschina.net/u/4585596/blog/4557594