文章目录
在java的数据结构中有一个数据结构就是队列,在这个特性里,Java提供了一些不同特点的队列面对不同的场景使用,这里做一个简单的总结。
1. 接口Queue<E>
这个是所有队列的父级接口,他提供了最基本的六个操作接口,这六个接口如下
Modifier and Type | Method and Description |
---|---|
boolean | add(E e) 将指定的元素插入到此队列中,如果可以立即执行此操作,而不会违反容量限制, true 在成功后返回 IllegalStateException 如果当前没有可用空间,则抛出IllegalStateException。 |
E | element() 检索,但不删除,这个队列的头。 |
boolean | offer(E e) 如果在不违反容量限制的情况下立即执行,则将指定的元素插入到此队列中。 |
E | peek() 检索但不删除此队列的头,如果此队列为空,则返回 null 。 |
E | poll() 检索并删除此队列的头,如果此队列为空,则返回 null 。 |
E | remove() 检索并删除此队列的头。 |
下面通过一个阻塞队列测试上面的六种方法
1.1. 添加操作
在添加操作中提供了两个方法,一个add方法和一个offer方法。
1.1.1. add
这个方法在超出容量的时候或者说添加失败的时候会抛出异常;
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue.add("1")));
log.info(String.valueOf(blockingQueue.add("2")));
log.info(String.valueOf(blockingQueue.add("3")));
log.info(String.valueOf(blockingQueue.add("4")));
}
}
打印结果
Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312)
at juc.queue.TestBlockingQueue.main(TestBlockingQueue.java:21)
14:41:22.379 [main] INFO juc.queue.TestBlockingQueue - true
14:41:22.381 [main] INFO juc.queue.TestBlockingQueue - true
14:41:22.381 [main] INFO juc.queue.TestBlockingQueue - true
这里提示队列满了,添加失败
1.1.2. offer
在添加失败后返回false,不会抛出异常
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue.offer("1")));
log.info(String.valueOf(blockingQueue.offer("2")));
log.info(String.valueOf(blockingQueue.offer("3")));
log.info(String.valueOf(blockingQueue.offer("4")));
}
}
打印结果:
14:45:34.479 [main] INFO juc.queue.TestBlockingQueue - true
14:45:34.481 [main] INFO juc.queue.TestBlockingQueue - true
14:45:34.481 [main] INFO juc.queue.TestBlockingQueue - true
14:45:34.481 [main] INFO juc.queue.TestBlockingQueue - false
和上面一样的操作,但是当队列满了后,添加失败,但是没有抛出异常,而是返回false
1.2. 检索操作
在队列中提供了两个用于检索头部元素的方法,检索方法在查询的时候是不会移除元素的,element和peek方法;
案例如下:
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue.add("1")));
log.info(String.valueOf(blockingQueue.add("2")));
log.info(String.valueOf(blockingQueue.add("3")));
log.info(String.valueOf(blockingQueue.element()));
log.info(String.valueOf(blockingQueue.peek()));
}
}
测试结果:
14:51:12.139 [main] INFO juc.queue.TestBlockingQueue - true
14:51:12.142 [main] INFO juc.queue.TestBlockingQueue - true
14:51:12.142 [main] INFO juc.queue.TestBlockingQueue - true
14:51:12.142 [main] INFO juc.queue.TestBlockingQueue - 1
14:51:12.142 [main] INFO juc.queue.TestBlockingQueue - 1
这两个方法只是访问了头部元素,但是没有移除元素;
这两个不同的地方是检索不成功的响应
1.2.1. element
这个方法在访问空的头部的时候会抛出异常:
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue.element()));
}
}
测试结果:
Exception in thread "main" java.util.NoSuchElementException
at java.util.AbstractQueue.element(AbstractQueue.java:136)
at juc.queue.TestBlockingQueue.main(TestBlockingQueue.java:18)
这里抛出了NoSuchElementException
异常
1.2.2. peek
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue.peek()));
}
}
测试结果:
[main] INFO juc.queue.TestBlockingQueue - null
这里返回的是一个null,没有抛出异常
1.3. 移除操作
队列也提供了俩个移除操作,分别的remove和poll方法,这两个方法在检索的同时也会移除检索的数据,下面案例:
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue.add("1")));
log.info(String.valueOf(blockingQueue.add("2")));
log.info(String.valueOf(blockingQueue.add("3")));
log.info(String.valueOf(blockingQueue.remove()));
log.info(String.valueOf(blockingQueue.poll()));
// 查看此时的头部元素
log.info(String.valueOf(blockingQueue.element()));
}
}
测试结果:
15:01:28.546 [main] INFO juc.queue.TestBlockingQueue - true
15:01:28.549 [main] INFO juc.queue.TestBlockingQueue - true
15:01:28.549 [main] INFO juc.queue.TestBlockingQueue - true
15:01:28.549 [main] INFO juc.queue.TestBlockingQueue - 1
15:01:28.549 [main] INFO juc.queue.TestBlockingQueue - 2
15:01:28.549 [main] INFO juc.queue.TestBlockingQueue - 3
从这里可以看出来返回了检索的结果,同时也移除了这个值。
但是这个两个不同在于面对移除对象不存在时的反应
1.3.1. remove
这个方法在访问null的时候会抛出异常
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue.remove()));
}
}
测试结果:
Exception in thread "main" java.util.NoSuchElementException
at java.util.AbstractQueue.remove(AbstractQueue.java:117)
at juc.queue.TestBlockingQueue.main(TestBlockingQueue.java:18)
这里返回了和检索的时候element方法一样的异常
1.3.2. poll
这个方法面对null的时候不会抛出异常,只会返回null
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue.poll()));
}
}
测试结果:
[main] INFO juc.queue.TestBlockingQueue - null
这里返回的是null,没有抛出异常
1.4. 总结
这里通过一个表格来说明这个问题:
添加 | 检索 | 移除 | |
---|---|---|---|
抛出异常 | add | element | remove |
不抛出异常 | offer | peek | poll |
2. 接口BlockingQueue<E>
在聊到队列的时候一定离不开这个阻塞队列接口,这个接口在继承了Queue接口后提供了这几个方法,下面说一下
Modifier and Type | Method and Description |
---|---|
boolean | contains(Object o) 如果此队列包含指定的元素,则返回 true 。 |
int | drainTo(Collection<? super E> c) 从该队列中删除所有可用的元素,并将它们添加到给定的集合中。 |
int | drainTo(Collection<? super E> c, int maxElements) 最多从该队列中删除给定数量的可用元素,并将它们添加到给定的集合中。 |
boolean | offer(E e, long timeout, TimeUnit unit) 将指定的元素插入到此队列中,等待指定的等待时间(如有必要)才能使空间变得可用。 |
E | poll(long timeout, TimeUnit unit) 检索并删除此队列的头,等待指定的等待时间(如有必要)使元素变为可用。 |
void | put(E e) 将指定的元素插入到此队列中,等待空间可用。 |
int | remainingCapacity() 返回该队列最好可以(在没有存储器或资源约束)接受而不会阻塞,或附加的元素的数量 Integer.MAX_VALUE 如果没有固有的限制。 |
boolean | remove(Object o) 从该队列中删除指定元素的单个实例(如果存在)。 |
E | take() 检索并删除此队列的头,如有必要,等待元素可用。 |
2.1. contains
这个方法非常好理解,就是检查十分包含指定元素
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue.add("1")));
log.info(String.valueOf(blockingQueue.add("2")));
log.info(String.valueOf(blockingQueue.add("3")));
log.info("===========================================");
log.info(String.valueOf(blockingQueue.contains("2")));
log.info(String.valueOf(blockingQueue.contains("4")));
}
}
测试结果:
15:40:31.832 [main] INFO juc.queue.TestBlockingQueue - true
15:40:31.834 [main] INFO juc.queue.TestBlockingQueue - true
15:40:31.834 [main] INFO juc.queue.TestBlockingQueue - true
15:40:31.834 [main] INFO juc.queue.TestBlockingQueue - ===========================================
15:40:31.834 [main] INFO juc.queue.TestBlockingQueue - true
15:40:31.834 [main] INFO juc.queue.TestBlockingQueue - false
2.2. drainTo
这个方法有一个两个重载方法,作用是取队列里的元素到给定的容器中。
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue1 = new ArrayBlockingQueue(3);
BlockingQueue<String> blockingQueue2 = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue1.add("1")));
log.info(String.valueOf(blockingQueue1.add("2")));
log.info(String.valueOf(blockingQueue1.add("3")));
log.info(String.valueOf(blockingQueue2.add("1")));
log.info(String.valueOf(blockingQueue2.add("2")));
log.info(String.valueOf(blockingQueue2.add("3")));
log.info("===========================================");
// 取出队列中所有的元素,返回取出元素的个数
List<String> list1 = new ArrayList<>();
log.info(String.valueOf(blockingQueue1.drainTo(list1)));
// 取出队列中指定个数的元素,返回取出元素的个数
List<String> list2 = new ArrayList<>();
List<String> list3 = new ArrayList<>();
log.info(String.valueOf(blockingQueue2.drainTo(list2,2)));
log.info(String.valueOf(blockingQueue2.drainTo(list3,2)));
log.info(JSON.toJSONString(list1));
log.info(JSON.toJSONString(list2));
log.info(JSON.toJSONString(list3));
}
}
测试结果
15:48:28.827 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.830 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.830 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.830 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.830 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.830 [main] INFO juc.queue.TestBlockingQueue - true
15:48:28.831 [main] INFO juc.queue.TestBlockingQueue - ===========================================
15:48:28.831 [main] INFO juc.queue.TestBlockingQueue - 3
15:48:28.831 [main] INFO juc.queue.TestBlockingQueue - 2
15:48:28.831 [main] INFO juc.queue.TestBlockingQueue - 1
15:48:28.880 [main] INFO juc.queue.TestBlockingQueue - ["1","2","3"]
15:48:28.880 [main] INFO juc.queue.TestBlockingQueue - ["1","2"]
15:48:28.880 [main] INFO juc.queue.TestBlockingQueue - ["3"]
从这个结果可以看出区别。
2.3. remainingCapacity 和 remove
这两个方法remainingCapacity 是计算剩余空间的,remove是原来方法的重载,用于指定删除元素
下面一个例子说明
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue.add("1")));
log.info(String.valueOf(blockingQueue.add("2")));
log.info(String.valueOf(blockingQueue.add("3")));
log.info("===========================================");
// 获取剩余空间
log.info(String.valueOf(blockingQueue.remainingCapacity()));
// 删除指定元素
log.info(String.valueOf(blockingQueue.remove("2")));
// 判断指定元素是否删除成功
log.info(String.valueOf(blockingQueue.contains("2")));
// 获取删除后队列的剩余空间
log.info(String.valueOf(blockingQueue.remainingCapacity()));
}
}
测试结果:
[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - ===========================================
[main] INFO juc.queue.TestBlockingQueue - 0
[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - false
[main] INFO juc.queue.TestBlockingQueue - 1
2.4. 等待操作
在阻塞队列中提供了两组添加和移除的阻塞等待操作,一组可以指定延时时间,一组不能指定时间,会一直阻塞下去。
添加 | 移除 | |
---|---|---|
指定时间 | offer | poll |
不指定时间 | put | take |
2.4.1. 指定时间操作 offer 和 poll
这两个方法是Queue接口的方法重载,可以设置最大阻塞时间
2.4.1.1. offer
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
log.info(String.valueOf(blockingQueue.add("1")));
log.info(String.valueOf(blockingQueue.add("2")));
log.info(String.valueOf(blockingQueue.add("3")));
new Thread(() -> {
try {
log.info(String.valueOf(blockingQueue.offer("4",1, TimeUnit.SECONDS)));
TimeUnit.SECONDS.sleep(2);
log.info(String.valueOf(blockingQueue.offer("4",2, TimeUnit.SECONDS)));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
).start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
log.info(String.valueOf(blockingQueue.remove()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
).start();
}
}
测试结果:
[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - true
[main] INFO juc.queue.TestBlockingQueue - true
[Thread-0] INFO juc.queue.TestBlockingQueue - false
[Thread-1] INFO juc.queue.TestBlockingQueue - 1
[Thread-0] INFO juc.queue.TestBlockingQueue - true
从上面可以看出,开始执行offer时阻塞1秒,发现无法添加后睡两秒,线程2在睡了4秒后移除一个元素,此时,线程1的offfer处于阻塞状态,发现移除一个元素了,有空间插入了,于是立即插入。第二次offer设置等待时间是2秒,实际等待了1秒,所有在最大允许的时间内,于是插入成功了,第一个则是等待1一秒后也没有空间插入,超时了,所以失败了。
2.4.1.1. poll
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(3);
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
log.info(String.valueOf(blockingQueue.add("1")));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
).start();
new Thread(() -> {
try {
log.info(String.valueOf(blockingQueue.poll(1,TimeUnit.SECONDS)));
log.info(String.valueOf(blockingQueue.poll(2,TimeUnit.SECONDS)));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
).start();
}
}
测试结果:
16:29:47.745 [Thread-1] INFO juc.queue.TestBlockingQueue - null
16:29:48.741 [Thread-1] INFO juc.queue.TestBlockingQueue - 1
16:29:48.741 [Thread-0] INFO juc.queue.TestBlockingQueue - true
从上面可以看出来,线程1取数据的时候等待一秒后发现没有值,所以取出了null,然后又设置了一个等待两秒的移除操作,线程0在睡两秒后添加了元素,此时等待的移除操作,立即执行了取,于是取出来元素。
2.4.2. 不指定时间操作 put和 take
@Slf4j
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue(2);
new Thread(() -> {
try {
log.info("阻塞获取元素 {}", blockingQueue.take());
TimeUnit.SECONDS.sleep(5);
log.info("等待5秒后移除一个元素 {}", blockingQueue.remove());
TimeUnit.SECONDS.sleep(1);
log.info("查看阻塞添加的元素 {}", blockingQueue.contains("4"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
, "A").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
log.info("等待1秒后添加一个元素 {}", blockingQueue.add("1"));
log.info("等待1秒后添加一个元素 {}", blockingQueue.add("2"));
TimeUnit.SECONDS.sleep(1);
log.info("再等待1秒后添加一个元素 {}", blockingQueue.add("3"));
log.info("阻塞添加一个元素");
blockingQueue.put("4");
log.info("阻塞添加一个元素成功");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
, "B").start();
}
}
测试结果:
[A] INFO juc.queue.TestBlockingQueue - 阻塞获取元素 1
[B] INFO juc.queue.TestBlockingQueue - 等待1秒后添加一个元素 true
[B] INFO juc.queue.TestBlockingQueue - 等待1秒后添加一个元素 true
[B] INFO juc.queue.TestBlockingQueue - 再等待1秒后添加一个元素 true
[B] INFO juc.queue.TestBlockingQueue - 阻塞添加一个元素
[A] INFO juc.queue.TestBlockingQueue - 等待5秒后移除一个元素 2
[A] INFO juc.queue.TestBlockingQueue - 查看阻塞添加的元素 true
[B] INFO juc.queue.TestBlockingQueue - 阻塞添加一个元素成功
查看结果会发现和现实不同,原因是日志输出在多线程下顺序有些颠倒,但是逻辑是对的,首先阻塞等待获取元素,1秒后添加元素,等待获取的操作会立即获取添加的元素,然后把队列填充满,再阻塞等待添加,等待5秒后移除一个元素,有空间了此时等待添加的操作会立即添加元素到队列中,然后检查这个元素,会发现添加成功。
3. 接口BlockingDeque<E>
这个是一个双端队列,也就是说可以在头尾添加或移除操作,因为它继承了阻塞队列接口,所以她有BlockingQueue的方法,这里就不再做介绍了,这里介绍它的其他方法
Modifier and Type | Method and Description |
---|---|
void | addFirst(E e) 插入此双端队列的前面,如果它是立即可行且不会违反容量限制,抛出一个指定的元素 IllegalStateException 如果当前没有空间可用。 |
void | addLast(E e) 在插入如果它是立即可行且不会违反容量限制,抛出此双端队列的末尾指定元素 IllegalStateException 如果当前没有空间可用。 |
Iterator<E> | iterator() 以正确的顺序返回此deque中的元素的迭代器。 |
boolean | offerFirst(E e) 插入此双端队列的前面,如果它是立即可行且不会违反容量限制,返回指定的元素 true 在成功和 false ,如果当前没有空间可用。 |
boolean | offerFirst(E e, long timeout, TimeUnit unit) 在此deque的前面插入指定的元素,等待指定的等待时间(如果需要空间可用)。 |
boolean | offerLast(E e) 插入此双端队列的末尾,如果它是立即可行且不会违反容量限制,返回指定的元素 true 在成功和 false ,如果当前没有空间可用。 |
boolean | offerLast(E e, long timeout, TimeUnit unit) 在此deque的末尾插入指定的元素,如果需要空间可用,等待指定的等待时间。 |
E | pollFirst(long timeout, TimeUnit unit) 检索并删除此deque的第一个元素,等待指定的等待时间(如有必要),使元素变为可用。 |
E | pollLast(long timeout, TimeUnit unit) 检索并删除此deque的最后一个元素,等待到指定的等待时间,如果需要,元素可用。 |
void | putFirst(E e) 在此deque的前面插入指定的元素,如有必要,等待空格变为可用。 |
void | putLast(E e) 在此deque的末尾插入指定的元素,如有必要,等待空格变为可用。 |
boolean | removeFirstOccurrence(Object o) 从此deque中删除指定元素的第一个出现。 |
boolean | removeLastOccurrence(Object o) 从此deque中删除指定元素的最后一次出现。 |
int | size() 返回此deque中的元素数。 |
E | takeFirst() 检索并删除此deque的第一个元素,如有必要等待,直到元素可用。 |
E | takeLast() 检索并删除此deque的最后一个元素,如有必要等待,直到元素可用。 |
这些方法就不一一演示了,这些基本都是阻塞队列方法的变种,实现头尾添加和移除操作,下面举一个例子说吗一些上面的部分方法。
@Slf4j
public class TestBlockingDeque {
public static void main(String[] args) {
BlockingDeque<String> blockingDeque = new LinkedBlockingDeque<>(4);
log.info("添加 {}", blockingDeque.offerFirst("1"));
log.info("添加 {}", blockingDeque.offerLast("2"));
log.info("添加 {}", blockingDeque.offerFirst("3"));
log.info("元素数量{}", blockingDeque.size());
Iterator<String> iterator = blockingDeque.iterator();
while (iterator.hasNext()){
log.info("iterator {}", iterator.next());
}
log.info("移除 {}", blockingDeque.pollLast());
log.info("移除 {}", blockingDeque.pollFirst());
log.info("移除 {}", blockingDeque.pollLast());
}
}
测试结果:
[main] INFO juc.queue.TestBlockingDeque - 添加 true
[main] INFO juc.queue.TestBlockingDeque - 添加 true
[main] INFO juc.queue.TestBlockingDeque - 添加 true
[main] INFO juc.queue.TestBlockingDeque - 元素数量3
[main] INFO juc.queue.TestBlockingDeque - iterator 3
[main] INFO juc.queue.TestBlockingDeque - iterator 1
[main] INFO juc.queue.TestBlockingDeque - iterator 2
[main] INFO juc.queue.TestBlockingDeque - 移除 2
[main] INFO juc.queue.TestBlockingDeque - 移除 3
[main] INFO juc.queue.TestBlockingDeque - 移除 1
从这个执行结果就可以看出这些方法是使用了,这里就不多说了。
4. SynchronousQueue<E>
其中每个插入操作必须等待另一个线程相应的删除操作,反之亦然。 同步队列没有任何内部容量,甚至没有一个容量。 你不能peek
在同步队列,因为一个元素,当您尝试删除它才存在; 您无法插入元素(使用任何方法),除非另有线程正在尝试删除它; 你不能迭代,因为没有什么可以迭代。 队列的头部是第一个排队的插入线程尝试添加到队列中的元素; 如果没有这样排队的线程,那么没有元素可用于删除,并且poll()
将返回null
。 为了其他Collection
方法(例如contains
)的目的, SynchronousQueue
充当空集合。 此队列不允许null
元素。
使用这个的时候是没有设置容量的,连续添加需要有其他线程取完后才可以继续添加新元素;
Modifier and Type | Method and Description |
---|---|
void | clear() 什么也没做。 |
boolean | contains(Object o) 始终返回 false 。 |
boolean | containsAll(Collection<?> c) 返回 false ,除非给定的集合为空。 |
int | drainTo(Collection<? super E> c) 从该队列中删除所有可用的元素,并将它们添加到给定的集合中。 |
int | drainTo(Collection<? super E> c, int maxElements) 最多从该队列中删除给定数量的可用元素,并将它们添加到给定的集合中。 |
boolean | isEmpty() 始终返回 true 。 |
Iterator<E> | iterator() 返回一个空的迭代器,其中 hasNext 总是返回 false 。 |
boolean | offer(E e) 如果另一个线程正在等待接收,则将指定的元素插入到此队列中。 |
boolean | offer(E e, long timeout, TimeUnit unit) 将指定的元素插入到此队列中,如果需要,等待另一个线程接收到的指定等待时间。 |
E | peek() 始终返回 null 。 |
E | poll() 如果另一个线程正在使一个元素可用,则检索并删除此队列的头。 |
E | poll(long timeout, TimeUnit unit) 检索并删除此队列的头,如果需要等待指定的等待时间,另一个线程插入它。 |
void | put(E e) 将指定的元素添加到此队列,等待另一个线程接收它。 |
int | remainingCapacity() 始终返回零。 |
boolean | remove(Object o) 总是返回 false 。 |
boolean | removeAll(Collection<?> c) 总是返回 false 。 |
boolean | retainAll(Collection<?> c) 总是返回 false 。 |
int | size() 始终返回零。 |
Spliterator<E> | spliterator() 返回一个空的拼写器,其中对 Spliterator.trySplit() 的调用总是返回 null 。 |
E | take() 检索并删除此队列的头,等待另一个线程插入它。 |
Object[] | toArray() 返回零长度数组。 |
<T> T[] | toArray(T[] a) 将指定数组的零元素设置为 null (如果数组的长度不为零)并返回。 |
从这里可以看到,许多方法都被禁止使用,返回的值是固定的,这里给一个使用案例,一个线程添加一个线程获取
@Slf4j
public class TestSynchronousQueue {
public static void main(String[] args) {
SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
synchronousQueue.put("1");
log.info("添加元素 1");
log.info("检索当前元素 {}",synchronousQueue.peek());
synchronousQueue.put("2");
log.info("添加元素 2");
log.info("检索当前元素 {}",synchronousQueue.peek());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
log.info("移除元素 {}", synchronousQueue.take());
TimeUnit.SECONDS.sleep(2);
log.info("移除元素 {}", synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "B").start();
}
}
返回结果:
[A] INFO juc.queue.TestSynchronousQueue - 添加元素 1
[A] INFO juc.queue.TestSynchronousQueue - 检索当前元素 null
[B] INFO juc.queue.TestSynchronousQueue - 移除元素 1
[A] INFO juc.queue.TestSynchronousQueue - 添加元素 2
[B] INFO juc.queue.TestSynchronousQueue - 移除元素 2
[A] INFO juc.queue.TestSynchronousQueue - 检索当前元素 null
从这些结果可以看出,peek方法被禁止了,返回总是空,源码:
public E peek() { return null; }
返回的结果可以看出,当一个线程取出当前的数据,才能添加,这个队列只能存放一个元素。
5. AbstractQueue<E> 非阻塞队列
这个队列是不能使用阻塞方法的,它没有想阻塞队列一样的阻塞操作。它的操作都是实时的
Modifier and Type | Method and Description |
---|---|
boolean | add(E e) 将指定的元素插入到此队列中,如果可以立即执行此操作而不违反容量限制, 则 在成功后返回 true, 如果当前没有可用空间,则抛出IllegalStateException。 |
boolean | addAll(Collection<? extends E> c) 将指定集合中的所有元素添加到此队列中。 |
void | clear() 从此队列中删除所有元素。 |
E | element() 检索,但不删除,这个队列的头。 |
E | remove() 检索并删除此队列的头。 |
测试案例
@Slf4j
public class TestAbstractQueue {
public static void main(String[] args) {
AbstractQueue<String> abstractQueue = new ArrayBlockingQueue<>(4);
log.info("{}", abstractQueue.add("1"));
List<String> list = Arrays.asList(new String[]{"2","3","4"});
log.info("{}", abstractQueue.addAll(list));
log.info("{}", abstractQueue.element());
log.info("{}", abstractQueue.remove());
log.info("{}", abstractQueue.peek());
abstractQueue.clear();
log.info("{}", abstractQueue.peek());
}
}
测试结果:
[main] INFO juc.queue.TestAbstractQueue - true
[main] INFO juc.queue.TestAbstractQueue - true
[main] INFO juc.queue.TestAbstractQueue - 1
[main] INFO juc.queue.TestAbstractQueue - 1
[main] INFO juc.queue.TestAbstractQueue - 2
[main] INFO juc.queue.TestAbstractQueue - null
这个案例说明了上面方法的使用,为什么这里要介绍这个队列,查看源码会发现阻塞队列是基于这个抽象类实现的,这个抽象类实现了add等抽象方法,告诉我们这个add方法是基于这个实现类的offer实现的。
6. 总结
这里介绍了一些主要的队列接口方法,通过他们也派生了许多其他的队列方法,这里就不过多的说明
从这幅图可以看出上面所说内容的关系了。