基于链接节点的可选有界阻塞队列。 此队列对元素进行 FIFO(先进先出)排序。 队列的头部是在队列中时间最长的元素。 队列的尾部是在队列中时间最短的元素。 新元素被插入到队列的尾部,队列检索操作获取队列头部的元素。 链接队列通常比基于数组的队列具有更高的吞吐量,但在大多数并发应用程序中性能更不可预测。
可选的容量绑定构造函数参数用作防止过度队列扩展的一种方式。 容量(如果未指定)等于 Integer.MAX_VALUE。 链接节点在每次插入时动态创建,除非这会使队列超出容量。
说明:官方源码中使用的是两个条件变量控制阻塞出队入队,本文中使用的是两个信号量对象来控制,注意区别与使用。
// 静态内部类Node
static class Node {
// 节点值
E item;
// next域
Node next;
// 构造器
Node(E e) {
item = e;
}
}
// 信号量,控制队列满入队和队列空出队
private Semaphore enSemaphore;
private Semaphore deSemaphore;
// 队列容量
private final int capacity;
// 队列元素个数
private int count;
// 队列头节点
Node head;
// 队列尾节点
Node tail;
/**
* Mini_LinkedBlockingQueue有参构造器
*
* @param capacity 队列容量
*/
public Mini_LinkedBlockingQueue111(int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException();
}
this.capacity = capacity;
tail = head = new Node(null);
enSemaphore = new Semaphore(capacity);
deSemaphore = new Semaphore(0);
}
/**
* 入队通用方法
*
* @param node 待入队节点
*/
private void enqueue(Node node) throws InterruptedException {
}
/**
* 通用出队方法
*
* @return 出队节点
*/
private E dequeue() {
return null;
}
/**
* 阻塞入队
*
* @param e 待入队元素
*/
public void put(E e) throws InterruptedException {
}
/**
* 非阻塞入队
*
* @param e 待入队元素
* @return 是否入队成功
*/
public boolean offer(E e) throws InterruptedException {
return false;
}
/**
* 阻塞出队
*
* @return 出队元素
*/
public E take() throws InterruptedException {
return null;
}
/**
* 非阻塞出队
*
* @return 出队元素
*/
public E poll() throws InterruptedException {
return null;
}
/**
* 入队通用方法
*
* @param node 待入队节点
*/
private void enqueue(Node node) throws InterruptedException {
// 若第一次添加元素则初始化head与tail
if (count == 0) {
head = tail = node;
count++;
} else {
// 尾节点next指向新节点
tail.next = node;
// 更新尾节点
tail = tail.next;
count++;
}
}
/**
* 通用出队方法
*
* @return 出队节点
*/
private E dequeue() {
// 获取头节点
Node h = head;
// 保存头节点的下一节点
Node next = h.next;
// 原头节点next域指向自己
h.next = h;
// 更新头节点
head = next;
// 获取待出队节点值保存后置null
E e = h.item;
h.item = null;
count--;
if (count == 0) {
head = tail = null;
}
return e;
}
/**
* 阻塞入队
*
* @param e 待入队元素
*/
public void put(E e) throws InterruptedException {
// 入参检查
if (e == null) {
throw new NullPointerException();
}
Node newNode = new Node<>(e);
// 信号量检查,若队满则阻塞
enSemaphore.acquire();
enqueue(newNode);
// 唤醒正在等待获取队列空的线程
deSemaphore.release();
}
/**
* 非阻塞入队
*
* @param e 待入队元素
* @return 是否入队成功
*/
public boolean offer(E e) throws InterruptedException {
if (e == null) {
throw new NullPointerException();
}
Node newNode = new Node<>(e);
// 若队满则直接返回
if (count == capacity) {
return false;
} else {
// 队未满则入队
enqueue(newNode);
// 信号量变更
enSemaphore.acquire();
deSemaphore.release();
return true;
}
}
/**
* 阻塞出队
*
* @return 出队元素
*/
public E take() throws InterruptedException {
// 信号量变更,队列空则阻塞
deSemaphore.acquire();
enSemaphore.release();
E e = dequeue();
return e;
}
/**
* 非阻塞出队
*
* @return 出队元素
*/
public E poll() throws InterruptedException {
// 若队列空则返回null
if (count == 0) {
return null;
} else {
// 否则返回出队元素
E e = dequeue();
// 信号量变更
deSemaphore.acquire();
enSemaphore.release();
return e;
}
}
private void print(Mini_LinkedBlockingQueue queue) {
Mini_LinkedBlockingQueue.Node head = queue.head;
for (int i = 0; i < queue.getCount(); i++) {
if (head != null && head.next != null) {
System.out.println(head.item + " --> " + head.next.item);
head = head.next;
} else if (head != null) {
System.out.println(head.item + " --> null");
}
}
}
@Test
public void put() throws InterruptedException {
Mini_LinkedBlockingQueue queue = new Mini_LinkedBlockingQueue<>(3);
queue.put("hello1");
queue.put("hello2");
queue.put("hello3");
print(queue);
new Thread(() -> {
try {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
System.out.println(Thread.currentThread().getName() + "线程正在put...");
queue.put("hello4");
stopWatch.stop();
System.out.println("等到poll了,入队成功,花费时间:" + stopWatch.getLastTaskTimeMillis() + "ms");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1").start();
Thread.sleep(1000);
new Thread(() -> {
try {
queue.poll();
System.out.println(Thread.currentThread().getName() + "线程poll了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t2").start();
/*
hello1 --> hello2
hello2 --> hello3
hello3 --> null
t1线程正在put...
t2线程poll了
等到poll了,入队成功,花费时间:1000ms
*/
}
@Test
public void offer() throws InterruptedException {
Mini_LinkedBlockingQueue queue = new Mini_LinkedBlockingQueue<>(3);
for (int i = 0; i < 4; i++) {
boolean offer = queue.offer("hello" + (i + 1));
System.out.println("第" + (i + 1) + "次入队成功了吗?" + offer);
}
/*
第1次入队成功了吗?true
第2次入队成功了吗?true
第3次入队成功了吗?true
第4次入队成功了吗?false
*/
}
@Test
public void take() throws InterruptedException {
Mini_LinkedBlockingQueue queue = new Mini_LinkedBlockingQueue<>(3);
new Thread(() -> {
try {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
System.out.println(Thread.currentThread().getName() + "线程正在take...");
String take = queue.take();
stopWatch.stop();
System.out.println("等到offer了,出队成功,花费时间:" + stopWatch.getLastTaskTimeMillis() + "ms" + " result:" + take);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1").start();
Thread.sleep(1000);
new Thread(() -> {
try {
queue.offer("hello");
System.out.println(Thread.currentThread().getName() + "线程offer了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t2").start();
/*
t1线程正在take...
t2线程offer了
等到offer了,出队成功,花费时间:1008ms
*/
}
@Test
public void poll() throws InterruptedException {
Mini_LinkedBlockingQueue queue = new Mini_LinkedBlockingQueue<>(3);
queue.put("hello1");
queue.put("hello2");
queue.put("hello3");
for (int i = 0; i < 4; i++) {
String poll = queue.poll();
System.out.println("第" + (i + 1) + "次出队" + poll);
}
/*
第1次出队hello1
第2次出队hello2
第3次出队hello3
第4次出队null
*/
}
在构造这个类的时候还是遇到了许多BUG,还是自己对于链表结构不是很熟悉的缘故。
控制阻塞入队出队还可以参考其他方式: