简介
队列是一种先进先出(FIFO)的数据结构,阻塞队列在基本操作的基础上支持插入阻塞(队列已满)和移除阻塞(队列为空)。
阻塞队列元素不能为null,因为null值会作为队列为空时 poll和peek接口的返回值 。并且应该设置边界,如果没有指定容量大小,默认容量是Integer.MAX_VALUE
java中使用接口 BlockingQueue来表示阻塞队列,它具有众多的实现类,juc包下的主要有以下实现类
- ArrayBlockingQueue : 基于数组的有界阻塞队列
- LinkedBlockingQueue: 基于链表的有界(无参实例默认大小为:Integer.MAX_VALUE,也可以说是无界队列 )阻塞队列
- PriorityBlockingQueue: 支持优先级排序的无界阻塞队列
- SynchronousQueue: 不存储元素的阻塞队列
- DelayQueue
- …
操作方法
阻塞队列中提供的方法可以分为四类:
- 抛出异常
- 立即返回特定的值(null /false)
- 阻塞当前线程
- 阻塞指定的时间
一、插入元素
BlockingQueue 提供了以下方法往队列中添加元素 add、offer、put。
注意:
- 不能往队列中添加null元素
- 元素添加到队列尾部(追加)。
add
/**
* Inserts the specified element into this queue if it is possible to do
* so immediately without violating capacity restrictions, returning
* {@code true} upon success and throwing an
* {@code IllegalStateException} if no space is currently available.
* When using a capacity-restricted queue, it is generally preferable to
* use {@link #offer(Object) offer}.
*
* @param e the element to add
* @return {@code true} (as specified by {@link Collection#add})
* @throws IllegalStateException if the element cannot be added at this
* time due to capacity restrictions
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this queue
* @throws NullPointerException if the specified element is null
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this queue
*/
boolean add(E e);
向队列中添加一个元素,添加成功返回true,失败(队列已满)抛出异常IllegalStateException。对于有界队列最好使用offer方法。
添加元素不能为null
public static void main(String[] args) {
// 容量为1的阻塞队列
BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<String>(1);
new Thread(()->{
try {
while(true){
Thread.sleep(1000);
System.out.println(blockingQueue.take());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
for(int i=0;i<3;i++){
//使用add 方法
boolean insertResult = blockingQueue.add("string... "+i);
System.out.println("insert result ...... "+insertResult);
}
}
Exception in thread “main” java.lang.IllegalStateException: Queue full
insert result … true
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312)
at thread.BlaockingQueueDemo.main(BlaockingQueueDemo.java:24)
string… 0
offer
offer方法是重载方法,有两种方式添加元素。
boolean offer(E e);
向队列中添加一个元素,添加成功返回true,添加失败返回false。
boolean offer(E e, long timeout, TimeUnit unit)
指定超时时间 ,在指定时间内插入元素返回true,否则返回false 。在等待过程中如果线程被中断(调用interrupt)会抛出InterruptedException
public static void main(String[] args) {
BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<String>(1);
new Thread(()->{
try {
while(true){
Thread.sleep(1000);
blockingQueue.take();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
for(int i=0;i<3;i++){
boolean insertResult = blockingQueue.offer("string... "+i);
System.out.println("insert result ...... "+insertResult);
}
}
insert result … true
insert result … false
insert result … false
public static void main(String[] args) {
ArrayBlockingQueue<String> blockingQueue=new ArrayBlockingQueue<String>(1);
new Thread(()->{
try {
while(true){
Thread.sleep(1000);
Object object =blockingQueue.take();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread mainThread = Thread.currentThread();
for(int i=0;i<3;i++){
boolean insertResult = false;
try {
//中断主线程
mainThread.interrupt();
boolean insetResult = blockingQueue.offer("A",500,TimeUnit.MILLISECONDS);
System.out.println("inert result .... "+ insetResult);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1220)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at java.util.concurrent.ArrayBlockingQueue.offer(ArrayBlockingQueue.java:374)
at thread.BlaockingQueueDemo.main(BlaockingQueueDemo.java:35)
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1220)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at java.util.concurrent.ArrayBlockingQueue.offer(ArrayBlockingQueue.java:374)
at thread.BlaockingQueueDemo.main(BlaockingQueueDemo.java:35)
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1220)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at java.util.concurrent.ArrayBlockingQueue.offer(ArrayBlockingQueue.java:374)
at thread.BlaockingQueueDemo.main(BlaockingQueueDemo.java:35)
put
/**
* Inserts the specified element into this queue, waiting if necessary
* for space to become available.
*
* @param e the element to add
* @throws InterruptedException if interrupted while waiting
* @throws ClassCastException if the class of the specified element
* prevents it from being added to this queue
* @throws NullPointerException if the specified element is null
* @throws IllegalArgumentException if some property of the specified
* element prevents it from being added to this queue
*/
void put(E e) throws InterruptedException;
当队列已满时,则该方法(调用该方法的线程)阻塞(等待)。添加元素不能为null
public static void main(String[] args) {
BlockingQueue<String> blockingQueue=new ArrayBlockingQueue<String>(1);
new Thread(()->{
try {
while(true){
Thread.sleep(1000);
blockingQueue.take();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
DateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for(int i=0;i<3;i++){
boolean insertResult = false;
try {
blockingQueue.put("string... "+i);
System.out.println("put...... "+format.format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
put… 2019-09-01 14:49:12
put… 2019-09-01 14:49:13
put… 2019-09-01 14:49:14
二、删除元素
移除元素的方法有: remove /poll /take。
移除元素方法根据移除位置分为两类:
- 移除头部元素 : poll, take
- 移除任意位置的元素 : remove(obj)
remove
/**
* Removes a single instance of the specified element from this queue,
* if it is present. More formally, removes an element {@code e} such
* that {@code o.equals(e)}, if this queue contains one or more such
* elements.
* Returns {@code true} if this queue contained the specified element
* (or equivalently, if this queue changed as a result of the call).
*
* @param o element to be removed from this queue, if present
* @return {@code true} if this queue changed as a result of the call
* @throws ClassCastException if the class of the specified element
* is incompatible with this queue
* (<a href="../Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified element is null
* (<a href="../Collection.html#optional-restrictions">optional</a>)
*/
boolean remove(Object o);
从队列中移除一个元素(最先匹配到的元素),移除成功返回true。如果队列中不存在该元素则返回false。
public static void main(String[] args) {
ArrayBlockingQueue<String> blockingQueue=new ArrayBlockingQueue<String>(5);
new Thread(()->{
try {
while(true){
Thread.sleep(1000);
Object object =blockingQueue.remove("A");
System.out.println(object);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
for(int i=0;i<3;i++){
boolean insertResult = false;
try {
blockingQueue.put("A");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
[A, A]
[A]
[]
[]
[]
…
poll
/**
* Retrieves and removes the head of this queue, waiting up to the
* specified wait time if necessary for an element to become available.
*
* @param timeout how long to wait before giving up, in units of
* {@code unit}
* @param unit a {@code TimeUnit} determining how to interpret the
* {@code timeout} parameter
* @return the head of this queue, or {@code null} if the
* specified waiting time elapses before an element is available
* @throws InterruptedException if interrupted while waiting
*/
E poll(long timeout, TimeUnit unit)
throws InterruptedException;
从队列中获取并移除队列头部元素。如果队列为空则方法(调用该方法的线程) 等待指定的时间,如果在此期间还是未获取元素则返回 null。等待时被中断则抛出异常InterruptedException
public static void main(String[] args) {
ArrayBlockingQueue<String> blockingQueue=new ArrayBlockingQueue<String>(5);
new Thread(()->{
try {
while(true){
Thread.sleep(1000);
Object object =blockingQueue.poll(1,TimeUnit.SECONDS);
System.out.println(object);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
for(int i=0;i<3;i++){
boolean insertResult = false;
try {
blockingQueue.put("A");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
A
A
A
null
null
…
take
/**
* Retrieves and removes the head of this queue, waiting if necessary
* until an element becomes available.
*
* @return the head of this queue
* @throws InterruptedException if interrupted while waiting
*/
E take() throws InterruptedException;
获取并移除队列头部的元素,如果队列为空则方法(调用该方法的线程)阻塞(等待)。阻塞时被中断则抛出异常InterruptedException
public static void main(String[] args) {
ArrayBlockingQueue<String> blockingQueue=new ArrayBlockingQueue<String>(5);
new Thread(()->{
try {
while(true){
Thread.sleep(1000);
Object object =blockingQueue.take();
System.out.println(blockingQueue);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
for(int i=0;i<3;i++){
boolean insertResult = false;
try {
blockingQueue.put("A");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
[A, A]
[A]
[]
因为队列中不存在元素时会阻塞。 所以while循环就不会进行执行下去。
小结
-
添加元素方法:
添加元素不能为null,否则抛出 NullPointerException法名 返回值 是否阻塞 抛出异常 超时等待 add true IllegalStateException ( 队列已满) offer true | false offer(E, long, TimeUnit) true | false ? InterruptedException(等待时被中断) √(等待指定的时间) put void √ InterruptedException(阻塞时被中断) 如果想要立即返回插入结果,推荐使用
offer
方法 -
移除元素方法
法名 返回值 是否阻塞 抛出异常 超时等待 remove() true NoSuchElementException(元素不存在) remove(e) true | false poll E | null poll(long,TimeUnit) E | null ? InterruptedException(等待时被中断) √(等待指定时间) take E √ InterruptedException(阻塞时被中断)
小结:
a. remove() 方法和 add(E) 方法对应,操作成功返回true,操作失败抛出异常
b. poll 方法 和 offer(E) 方法对应,操作成功返回true,操作失败返回false。
c. offer(E, long, TimeUnit) 和 poll(long,TimeUnit) 方法对应,操作成功立即返回结果,操作失败会等待指定的时间再尝试操作
d. put 和 take 方法对应, 操作成功立即返回(void),操作失败一直等待。
e. 操作失败是指,向已满的队列中添加元素,从已空的队列中移除元素。
注:
E: 表示移除元素
remove() 方法不是BlockingQueue 接口中的方法,remove(e) 才是。
此处的阻塞含义和线程状态中的阻塞含义并不相同,此处的阻塞对应线程中的 WAITING
状态,超时等待 对应TIMED_WAITING
。