java 手写阻塞队列_实现Java 阻塞队列

实现Java 阻塞队列

在自己实现之前先搞清楚阻塞队列的几个特点:

基本队列特性:先进先出。

写入队列空间不可用时会阻塞。

获取队列数据时当队列为空时将阻塞。

实现队列的方式多种,总的来说就是数组和链表;其实我们只需要搞清楚其中一个即可,不同的特性主要表现为数组和链表的区别。

这里的 ArrayBlockingQueue 看名字很明显是由数组实现。

我们先根据它这三个特性尝试自己实现试试。

初始化队列

我这里自定义了一个类:ArrayBlockQueue,它的构造函数如下:

//队列参数

public int size;

private volatile Object[] items;

private volatile int pollPoint = 0;

private volatile int addPoint = 0;

private volatile int count = 0;

//初始化队列容量

public ArrayBlockQueue(int size) {

this.size = size;

this.items = new Object[size];

}

写入操作

有几个需要注意的点:

队列满的时候,写入的线程需要被阻塞。

写入过队列的数量大于队列大小时需要从第一个下标开始写。

先看第一个队列满的时候,写入的线程需要被阻塞,先来考虑下如何才能使一个线程被阻塞,看起来的表象线程卡住啥事也做不了。

其实这样的一个特点很容易让我们想到 Java 的等待通知机制来实现线程间通信。

所以我这里的做法是,一旦队列满时就将写入线程调用 addLock.wait() 进入 waiting 状态,直到空间可用时再进行唤醒。

初始化 两个锁

//初始化两个锁

private Object addLock = new Object();

private Object pollLock = new Object();

写入操作代码

public void add(Object item) {

synchronized (addLock) {

//队列满则阻塞

while (count >= size) {

try {

addLock.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

items[addPoint] = item;

addPoint = (addPoint + 1) % size;

count++;

//当队列从0个元素添加到1个元素,则说明从空状态转非空状态可以通知一次取元素线程

if (count == 1)

synchronized (pollLock) {

pollLock.notifyAll();

}

}

}

所以这里声明了两个对象用于队列满、空情况下的互相通知作用。

在写入数据成功后需要使用 pollLock.notifyAll(),这样的目的是当获取队列为空时,一旦写入数据成功就可以把消费队列的线程唤醒。

这里的 wait 和 notify 操作都需要对各自的对象使用 synchronized 方法块,这是因为 wait 和 notifyAll 都需要获取到各自的锁。

消费队列

上文也提到了:当队列为空时,获取队列的线程需要被阻塞,直到队列中有数据时才被唤醒。

代码和写入的非常类似,也很好理解;只是这里的等待、唤醒恰好是相反的。

总的来说就是:

写入队列满时会阻塞直到获取线程消费了队列数据后唤醒写入线程。

消费队列空时会阻塞直到写入线程写入了队列数据后唤醒消费线程。

public Object poll() {

synchronized (pollLock) {

//队列空则阻塞

while (count == 0) {

try {

pollLock.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

Object value = items[pollPoint];

pollPoint = (pollPoint + 1) % size;

count--;

//当队列从满到非满,可以通知一次增添元素的线程

if (count == (size - 1))

synchronized (addLock) {

addLock.notifyAll();

}

return value;

}

}

测试

每一秒出队1个

//消费者

//每一秒出队1个

class Counsum extends Thread {

private ArrayBlockQueue queue;

public Counsum(ArrayBlockQueue queue) {

this.queue = queue;

}

@Override

public void run() {

while (true) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + " 出队一个数据: " + queue.poll());

}

}

}

每5秒入队2个,可见生产明显慢于消费

//生产者

//每5秒入队2个

class Product extends Thread {

private ArrayBlockQueue queue;

public Product(ArrayBlockQueue queue) {

this.queue = queue;

}

@Override

public void run() {

int i = 0;

while (true) {

try {

Thread.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

for (int j = 0; j < 2; j++, i++) {

queue.add(i);

System.out.println(Thread.currentThread().getName() + "【入队一个数据: " + i+"】");

}

}

}

}

main

public class Test extends Object {

public static void main(String[] args) throws CloneNotSupportedException {

ArrayBlockQueue queue = new ArrayBlockQueue(5);

Thread counsum = new Counsum(queue);

Thread counsum1 = new Counsum(queue);

Thread counsum2 = new Counsum(queue);

Thread product = new Product(queue);

Thread product1 = new Product(queue);

Thread product2 = new Product(queue);

counsum.start();

product.start();

counsum1.start();

product1.start();

counsum2.start();

product2.start();

}

}

//output

消费者线程-1出队一个数据: 0

生成者线程-3入队一个数据: 0

消费者线程-3出队一个数据: 0

消费者线程-2出队一个数据: 0

生成者线程-2入队一个数据: 0

生成者线程-1入队一个数据: 0

生成者线程-2入队一个数据: 1

生成者线程-3入队一个数据: 1

生成者线程-1入队一个数据: 1

消费者线程-3出队一个数据: 1

消费者线程-1出队一个数据: 1

消费者线程-2出队一个数据: 1

消费者线程-2出队一个数据: 2

生成者线程-2入队一个数据: 2

消费者线程-1出队一个数据: 2

生成者线程-3入队一个数据: 2

生成者线程-1入队一个数据: 2

消费者线程-3出队一个数据: 2

生成者线程-1入队一个数据: 3

生成者线程-3入队一个数据: 3

生成者线程-2入队一个数据: 3

消费者线程-1出队一个数据: 3

消费者线程-3出队一个数据: 3

消费者线程-2出队一个数据: 3

使用lock锁版本

package concurrent;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

public class ArrayBlockQueue {

//队列参数

public int size;

private volatile Object[] items;

private volatile int pollPoint = 0;

private volatile int addPoint = 0;

private volatile int count = 0;

//初始化两个锁

private ReentrantLock addLock = new ReentrantLock();

private ReentrantLock pollLock = new ReentrantLock();

//初始化两个信号量

private Condition full =addLock.newCondition();

private Condition empty =pollLock.newCondition();

//初始化队列容量

public ArrayBlockQueue(int size) {

this.size = size;

this.items = new Object[size];

}

public void add(Object item) {

addLock.lock();

{

//队列满则阻塞

while (count >= size) {

try {

full.await();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

items[addPoint] = item;

addPoint = (addPoint + 1) % size;

count++;

//当队列从0个元素添加到1个元素,则说明从空状态转非空状态可以通知一次取元素线程

if (count == 1) {

pollLock.lock();

empty.signal();

pollLock.unlock();

}

}

addLock.unlock();

}

public Object poll() {

Object value;

pollLock.lock();

{

//队列空则阻塞

while (count == 0) {

try {

empty.await();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

value = items[pollPoint];

pollPoint = (pollPoint + 1) % size;

count--;

//当队列从满到非满,可以通知一次增添元素的线程

if (count == (size - 1)) {

addLock.lock();

full.signal();

addLock.unlock();

}

}

pollLock.unlock();

return value;

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值