实现基于链表的阻塞队列,并使用两个锁实现同时进行写和读操作:
核心方法:
1、放入数据
- offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false.(本方法不阻塞当前执行方法的线程)
- offer(E o, long timeout, TimeUnit unit):可以设定等待的时间,如果在指定的时间内,还不能往队列中加入BlockingQueue,则返回失败。
- put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续.
2、获取数据
- poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null;
- poll(long timeout, TimeUnit unit):从BlockingQueue取出一个队首的对象,如果在指定时间内,队列一旦有数据可取,则立即返回队列中的数据。否则知道时间超时还没有数据可取,返回失败。
- take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入;
- drainTo():一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数),通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。
实现逻辑:
这里主要实现put和take方法
Put写入数据逻辑:
1、检测队列是否已满,若满则阻塞,否则添加数据,并唤醒另一个写入线程
2、唤醒读取线程
Take读取数据逻辑:
1、检测队列是否为空,若空则阻塞,否则读取数据,并唤醒另一个读取线程
2、唤醒写入线程
注意:并发环境下,队列目前拥有的数据要使用AtomicInteger自增。
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyBlockingQueue<E> {
private Node<E> head;
private Node<E> last;
private static class Node<E> {
E item;
Node<E> next;
Node(Node<E> prev, E element) {
this.item = element;
this.next = next;
}
Node(E element) {
this.item = element;
}
}
/** 队列拥有的元素 */
private AtomicInteger ac = new AtomicInteger(0);
/** 队列容量 */
private int capacity;
private final ReentrantLock writeLock = new ReentrantLock();
private final Condition writeSignal = writeLock.newCondition();
private final ReentrantLock readLock = new ReentrantLock();
private final Condition readSignal = readLock.newCondition();
public MyBlockingQueue(int num) {
this.capacity = num;
last = head = new Node<E>(null);
}
private void callReadLock() {
readLock.lock();
readSignal.signal();
readLock.unlock();
}
private void callWriteLock() {
writeLock.lock();
writeSignal.signal();
writeLock.unlock();
}
//调用此方法的线程被阻断直到BlockingQueue里面有空间再继续.阻塞
void put(E i) throws InterruptedException {
Node<E> node = new Node<E>(i);
int count = -1;
writeLock.lockInterruptibly();
try {
//若队列已满,则线程阻塞
while (ac.get() == capacity) {
writeSignal.await();
}
last.next = node;
last = last.next;
count = ac.getAndIncrement();
//队列尚有位置,则发出信号,继续添加元素
if (count + 1 < capacity)
writeSignal.signal();
} finally {
writeLock.unlock();
}
//若队列中有元素,则发出信号,消费元素
if (count >= 1)
callReadLock();
}
//不阻塞当前执行方法的线程,不阻塞
void offer(E i) {
}
//取不到时阻塞
E take() throws InterruptedException {
E x;
int count = -1;
readLock.lockInterruptibly();
try {
//若队列为空,则阻塞线程
while (ac.get() == 0) {
readSignal.await();
}
Node<E> h = head;
Node<E> first = h.next;
h.next = h;
head = first;
x = first.item;
first.item = null;
count = ac.getAndDecrement();
//若队列尚有元素,则唤醒消费线程
if (count > 1)
readSignal.signal();
} finally {
readLock.unlock();
}
//若队列的数据没满,发出信号唤醒添加元素线程
if (count <= capacity-1)
callWriteLock();
return x;
}
//取不到时返回null,不阻塞
E poll() {
E x;
readLock.lock();
Node<E> h = head;
Node<E> first = h.next;
h.next = h;
head = first;
x = first.item;
first.item = null;
ac.getAndDecrement();
readLock.unlock();
return x;
}
int getCount() {
return ac.get();
}
}
测试方法:3个线程写入数据,2个线程读取数据
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyBlockingQueueTest {
public static void main(String[] args) throws InterruptedException {
MyBlockingQueue<String> queue = new MyBlockingQueue<>(100);
queue.put("第一个元素");
ExecutorService es = Executors.newFixedThreadPool(10);
for (int i = 0; i < 3; i++) {
es.submit(() -> {
for (int j = 0; j < 10000; j++) {
try {
queue.put(Thread.currentThread().getName()+":"+j);
// Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
for (int i = 0; i < 2; i++) {
es.submit(() -> {
try {
while (true) {
System.out.println(Thread.currentThread().getName()+"取:"+queue.take());
// Thread.sleep(50);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
es.shutdown();
}
}