Java 中的几种阻塞队列 (1),mybatis 返回主键原理
Java 中的BlockingQueue接口是一个线程安全的存取队列,适用于生产者消费者的应用场景中,支持两个附加操作:
生产者线程会一直不断的往阻塞队列中放入数据,直到队列满了为止。队列满了后,生产者线程阻塞等待消费者线程取出数据。
消费者线程会一直不断的从阻塞队列中取出数据,直到队列空了为止。队列空了后,消费者线程阻塞等待生产者线程放入数据。
BlockingQueue提供四种不同的处理方法。
| ? | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
| --- | --- | --- | --- | --- |
| 插入方法 | add(o) | offer(o) | put(o) | offer(o, timeout, timeunit) |
| 移除方法 | remove(o) | poll() | take(o) | poll(o, timeout, timeunit) |
| 检查方法 | element() | peek() | — | — |
抛出异常:
add: 插入数据时,如果阻塞队列满,那么抛出异常
IllegalStateException,否则插入成功返回true。当使用有界(capacity-restricted queue)阻塞队列时,建议使用offer方法。IllegalStateException?- if the element cannot be added at this time due to capacity restrictionsClassCastException?- if the class of the specified element prevents it from being added to this queueNullPointerException?- if the specified element is nullIllegalArgumentException?- if some property of the specified element prevents it from being added to this queueremove: 删除数据时,如果队列中有此数据,删除成功返回
true,否则返回false。如果包含一个或者多个 object,那么只移除一个就返回true。注意:remove(o)是BlockingQueue接口的方法,remove()是Queue接口的方法。element: 如果队列为空,那么抛出异常
NoSuchElementException。如果队列不为空,查询返回队列头部的数据,但是不移除数据,这点不同于remove(),element同样是Queue接口的方法。
返回特殊值:
offer: 插入数据时,如果阻塞队列没满,那么插入成功返回
true,否则返回false。当使用有界(capacity-restricted queue)阻塞队列时,建议使用offer方法,不建议会抛出异常的add方法。poll: 此方法是
Queue接口的。如果队列不为空,查询、移除并返回队列头部元素。如果队列为空,那么返回null。peek: 此方法是
Queue接口的
。如果队列为空,返回null,这点不同于poll。如果队列不为空,查询返回队列头部的数据,但是不移除数据,这点不同于remove()。
一直阻塞:
put: 插入数据时,如果队列已满,那么阻塞等待队列可用,等待期间如果被中断,那么抛出
InterruptedException。take: 查询、删除并返回队列头部元素,如果队列为空,那么阻塞等待队列可用,等待期间如果被中断,那么抛出
InterruptedException。
超时退出:
offer: 插入数据时,如果队列已满,那么阻塞指定时间等待队列可用,等待期间如果被中断,那么抛出
InterruptedException。如果插入成功,那么返回true,如果在达到指定时间后仍然队列不可用,那么返回false。poll: 查询、删除并返回队列头部元素,如果队列为空,那么阻塞指定时间等待队列可用,等待期间如果被中断,那么抛出
InterruptedException。如果删除成功,那么返回队列头部元素,如果在达到指定时间后仍然队列不可用,那么返回null。
Queue队列不能插入 null,否则会抛出NullPointerException。
JDK7 提供了 7 个阻塞队列。分别是
ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
ArrayBlockingQueue
ArrayBlockingQueue是基于数组(array-based)的先进先出(FIFO)有界(bounded)阻塞队列。
创建队列时,必须要指定队列容量(capacity),即数组大小。
创建队列时,可以传入
Collection来初始化队列元素。队列一旦被创建,那么队列容量不能被改变。
队列支持公平模式和非公平模式,默认非公平模式。
队列中只有一把锁,写锁和读锁未分离,并发控制采用了经典的 two-condition(
notEmpty、notFull)算法。
LinkedBlockingQueue
LinkedBlockingQueue是基于链表(linked nodes)的先进先出(FIFO)的可选界(optionally-bounded)的阻塞队列。
创建队列时,为了避免额外开销,可以指定队列容量(capacity);如果不指定队列容量,那么默认队列容量为
Integer.MAX_VALUE。创建队列时,可以可以传入
Collection来初始化队列元素,此时不能指定队列容量,默认为Integer.MAX_VALUE。队列中的
count即当前队列元素个数,采用AtomicInteger,避免put和take的竞争。与
ArrayBlockingQueue不同的是,LinkedBlockingQueue队列中有两把锁,读锁和写锁是分离的。在使用
LinkedBlockingQueue时,若队列大小为默认值,且生产速度大于消费速度时,可能会内存溢出。LinkedBlockingQueue理论上来说比ArrayBlockingQueue有更高的吞吐量,但是在大多数的实际应用场景中,却没有很好的表现。
PriorityBlockingQueue
PriorityBlockingQueue是基于数组(array based)的支持优先级的无界(unbounded)的阻塞队列。此队列的数据结构是堆。
创建队列时,如果指定初始化容量(initialCapacity),那么默认初始化容量
DEFAULT_INITIAL_CAPACITY为 11。创建队列时,可以指定队列初始化容量(initialCapacity),不是队列容量(capacity)。
PriorityBlockingQueue的无界(unbounded)相对于LinkedBlockingQueue的可选界(optionally-bounded)来说,无界是指不能在创建队列时,不能指定队列的最大容量(capacity),并不是说PriorityBlockingQueue本身无界。LinkedBlockingQueue默认(注意,这里指的是默认容量,即,你可以指定大于Integer.MAX_VALUE的值)的最大容量是Integer.MAX_VALUE,而PriorityBlockingQueue的最大容量是MAX_ARRAY_SIZE=Integer.MAX_VALUE-8。PriorityBlockingQueue无界的另一个意思就是生产者线程不会因为队列满了就阻塞,因为队列是无界的,没有容量满了这一说。offer(E e, long timeout, TimeUnit unit)的后两个参数没有任何作用查看源代码发现,其方法的实现直接是调用了offer(e)。但是当队列为空时,take仍然会阻塞。offer(e)永远不会返回false,offer(E e, long timeout, TimeUnit unit)永远不会返回false或者阻塞。PriorityBlockingQueue通过数组来实现队列,在原有数组满了的情况下,通过复制数组来扩展队列容量,如果新扩展的数组容量大小超过MAX_ARRAY_SIZE,那么抛出OutOfMemoryError异常。默认情况下元素采取自然顺序排列,也可以通过比较器 comparator 来指定元素的排序规则。元素按照升序排列。
DelayQueue
DelayQueue是基于PriorityQueue实现的支持延时获取元素的阻塞队列。
DelayQueue中存放的对象必须实现Delayed接口。如果没有到期元素,那么就没有
head,poll方法返回 null。











评论