1. 背景
这里说的是 Java
的有界非阻塞队列,如果对有界非阻塞所具有的概念不太懂的话,可以参看 [[Java数据结构10-死磕Java队列-基础篇]] 。
本人对基本原理以及概念说的偏少,阐述的是应用过程,和一些使用上的注意事项。
2. LinkedList
List
接口与 Queue
同一级别,继承自 Collection
接口, LinkedList
实现了 Deque
接口。因为它的实现是双向链表,并没有初始化大小,更没有扩容的机制。
2.1. 说明
通常适用与插入元素和删除元素,时间的复杂度 O(1)
。它性能高效的原理是通过链表实现,插入和删除只需要改变前后两个节点指针指向即可。
2.2. 构造函数
LinkedList
提供了两种构造函数,分别为无参、有参。有参的构造函数提供的参数为几个集合。
/**
* Constructs an empty list.
*/
public LinkedList() {
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
2.3. 常用方法
方法名 | 描述 |
---|---|
element | 检索但不删除此列表的头(第一个元素) |
getFirst | 返回此列表中的第一个元素 |
getLast | 返回此列表中的最后一个元素 |
offer | 将指定的元素添加为此列表的尾部 |
poll | 检索并删除此列表的头(第一个元素) |
pop | 从此列表表示的堆栈中弹出一个元素,换句话说,删除并返回此列表的第一个元素 |
peek | 检索但不删除此列表的头(第一个元素) |
2.4. 样例
2.4.1. 单线程
此样例,我们创造一个队列,先查看它的头元素,然后再消费一条,再次查看它头元素的变化。
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
initQueue(queue);
for(String q : queue){
System.out.print(q+"\t");
}
System.out.print("\n");
System.out.println(" 队列中头信息element: "+queue.element());
System.out.println(" 获取信息poll: " + queue.poll());
System.out.println(" 队列中头信息element: "+queue.element());
System.out.println(" ============ ");
System.out.println(" 队列中头信息peek: "+queue.peek());
for(String q : queue){
System.out.print(q+"\t");
}
System.out.print("\n");
}
/** 初始化队列中的数据
* @Description
* @param queue
* @return void
* @throws
* @date 2020/8/5 15:12
*/
public static void initQueue(Queue<String> queue){
queue.offer("Q1");
queue.offer("Q2");
queue.offer("Q3");
queue.offer("Q4");
queue.offer("Q5");
}
我们 poll
操作后,元素的数量发生了变化。这是我们队列的最最基础的使用形式,满足常用的顺序性消费场景,解决一般的线性问题。
2.4.2. 多线程
实际运用中,我们将采用一个线程模拟生产者不断对队列进行创造,同时给队列设定一个容量的上限,超过队列容量,将对生产者线程进行挂起;一个线程模拟消费者对队列进行消费,当队列中没有可供元素进行消费,需要对消费者线程进行挂起。
因为 LinkedList
的特性,当消费者没有获取到元素,此时消费者线程不会阻塞,而是直接返回,这就是非阻断,此处强调下,因为后续阻塞队列与它有明确的区别。
2.4.3. 队列操作
public class QueueUtil {
LinkedList<String> queue;
private final static int size = 15;
QueueUtil(LinkedList<String> queue) {
this.queue = queue;
}
public void add(String val) throws InterruptedException {
synchronized (queue) {
if (queue.size() == size) {
System.out.println(Thread.currentThread().getName() + "队列已经满");
queue.wait();
} else {
queue.offer(val);
queue.notify();
}
}
}
public String romove() throws InterruptedException {
synchronized (queue) {
String val = null;
if (queue.size() == 0) {
System.out.println(Thread.currentThread().getName() + "队列内容不足");
queue.wait();
} else {
val = queue.pop();
System.out.println(Thread.currentThread().getName() + "队列内容弹出"+ val);
queue.notify();
}
return val;
}
}
}
2.4.3.1. 生产者
class ProducerCase implements Runnable {
private QueueUtil util;
ProducerCase(QueueUtil util){
this.util=util;
}
@SneakyThrows
@Override
public void run() {
for (int i = 0; i < 10; i++) {
util.add("Qu_"+i);
if(i%3==0){
TimeUnit.SECONDS.sleep(2);
}
}
}
}
2.4.3.2. 消费者
class ConsumberCase implements Runnable {
private QueueUtil util;
ConsumberCase(QueueUtil util){
this.util=util;
}
@SneakyThrows
@Override
public void run() {
for (int i = 0; i < 10; i++) {
String val = util.romove();
System.out.println(Thread.currentThread().getName() + "[ConsumberCase ]" + (val==null?"=================":val));
}
}
}
2.4.3.3. Main方法
public class MulitQueue {
public static void main(String[] args) {
LinkedList<String> queue = new LinkedList<>();
QueueUtil util = new QueueUtil(queue);
ExecutorService exe= Executors.newFixedThreadPool(6);
for (int i = 0; i < 2; i++) {
exe.submit(new ProducerCase(util));
}
for (int i = 0; i < 2; i++) {
exe.submit(new ConsumberCase(util));
}
exe.shutdown();
}
}
2.4.3.4. 日志
> Task :MulitQueue.main()
pool-1-thread-3队列内容弹出Qu_0
pool-1-thread-4队列内容弹出Qu_0
pool-1-thread-4[ConsumberCase ]Qu_0
pool-1-thread-4队列内容不足
pool-1-thread-3[ConsumberCase ]Qu_0
pool-1-thread-3队列内容不足
pool-1-thread-3[ConsumberCase ]=================
pool-1-thread-3队列内容弹出Qu_1
pool-1-thread-4[ConsumberCase ]=================
pool-1-thread-4队列内容弹出Qu_1
pool-1-thread-3[ConsumberCase ]Qu_1
pool-1-thread-4[ConsumberCase ]Qu_1
pool-1-thread-3队列内容弹出Qu_2
pool-1-thread-3[ConsumberCase ]Qu_2
pool-1-thread-4队列内容弹出Qu_3
pool-1-thread-4[ConsumberCase ]Qu_3
pool-1-thread-3队列内容弹出Qu_2
pool-1-thread-3[ConsumberCase ]Qu_2
pool-1-thread-4队列内容弹出Qu_3
pool-1-thread-4[ConsumberCase ]Qu_3
pool-1-thread-3队列内容不足
pool-1-thread-4队列内容不足
pool-1-thread-3[ConsumberCase ]=================
pool-1-thread-4[ConsumberCase ]=================
pool-1-thread-3队列内容弹出Qu_4
pool-1-thread-3[ConsumberCase ]Qu_4
pool-1-thread-4队列内容弹出Qu_4
pool-1-thread-4[ConsumberCase ]Qu_4
pool-1-thread-3队列内容弹出Qu_5
pool-1-thread-3[ConsumberCase ]Qu_5
pool-1-thread-4队列内容弹出Qu_6
pool-1-thread-4[ConsumberCase ]Qu_6
pool-1-thread-3队列内容弹出Qu_5
pool-1-thread-3[ConsumberCase ]Qu_5
pool-1-thread-4队列内容弹出Qu_6
pool-1-thread-4[ConsumberCase ]Qu_6
pool-1-thread-3队列内容不足
pool-1-thread-4队列内容不足
pool-1-thread-3[ConsumberCase ]=================
pool-1-thread-4[ConsumberCase ]=================
BUILD SUCCESSFUL in 9s
3 actionable tasks: 2 executed, 1 up-to-date
11:15:21: Task execution finished 'MulitQueue.main()'.