BlockingQueue阻塞队列源码
BlockingQueue介绍
- ArrayBlockingQueue: 一个由数组结构组成的有界阻塞队列。
- LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列。
- SynchronousQueue: 一个不存储元素的阻塞队列。
- PriorityBlockingQueue: 一个支持优先级排序的无界阻塞队列。
方法 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
---|---|---|---|---|
插入方法 | add | offer | put | offer |
删除方法 | remove | poll | take | poll |
BlockingQueue用法
带着疑问去使用,可能会更好:
- 队列空着的时候去拿数据会怎么样?
- 队列满着的时候去存数据会怎么样?
- 前面的方法,能否会出现阻塞或者非阻塞现象?
队列满了,存数据会怎么样?
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(10);
for (int i = 0 ; i < 15 ; i++) {
final int i1 = i;
Thread t1 = new Thread(() -> {
try {
blockingQueue.put(" msg " + i1);
System.out.println("======================= provider " + i1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
}
Thread t2 = new Thread(() -> {
try {
String msg = blockingQueue.take();
System.out.println("======================= consumer " + msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t2.start();
}
说明:队列满了,那么调用put方法,会将当前线程阻塞。
队列空了,取数据会怎么样?
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(10);
for (int i = 0 ; i < 5 ; i++) {
final int i1 = i;
Thread t1 = new Thread(() -> {
try {
blockingQueue.put(" msg " + i1);
System.out.println("======================= provider " + i1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
}
for (int j = 0 ; j < 10 ; j++) {
Thread t2 = new Thread(() -> {
try {
String msg = blockingQueue.take();
System.out.println("======================= consumer " + msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t2.start();
}
}
说明:队列空了,那么调用take方法,会将当前线程阻塞。
依次测试结果:
poll方法,在队列空了的时候,返回false,取出的对象为空,当前线程非阻塞。
offer方法,在队列满了的时候,返回false,当前线程非阻塞。
源码
ArrayBlockingQueue构造器初始化参数
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
public ArrayBlockingQueue(int capacity, boolean fair) {
// 容量参数小于0,报非法参数异常
if (capacity <= 0)
throw new IllegalArgumentException();
// 初始化存入元素的数组
this.items = new Object[capacity];
// 初始化非公平的重入锁
lock = new ReentrantLock(fair);
// 设置非空的等待唤醒队列
notEmpty = lock.newCondition();
// 设置非满的等待唤醒队列
notFull = lock.newCondition();
}
put阻塞存入数据方法
代码
public void put(E var1) throws InterruptedException {
checkNotNull(var1);
// 重入锁上锁
ReentrantLock var2 = this.lock;
var2.lockInterruptibly();
try {
// 当队列已经满了,那么就将当前线程等待
while(this.count == this.items.length) {
this.notFull.await();
}
// 进队列
this.enqueue(var1);
} finally {
// 重入锁解锁
var2.unlock();
}
}
private void enqueue(E var1) {
// 获取存放对象的数组
Object[] var2 = this.items;
// 将元素存入数组
var2[this.putIndex] = var1;
// 如果当前的下标已经超出了存放对象数组的长度
if (++this.putIndex == var2.length) {
this.putIndex = 0;
}
++this.count;
// 唤醒非空等待队列中的元素
this.notEmpty.signal();
}
take阻塞拿取数据方法
代码
public E take() throws InterruptedException {
ReentrantLock var1 = this.lock;
var1.lockInterruptibly();
Object var2;
try {
// 当前元素的个数已经为0
while(this.count == 0) {
// 当前线程等待
this.notEmpty.await();
}
// 出队列
var2 = this.dequeue();
} finally {
var1.unlock();
}
return var2;
}
private E dequeue() {
// 获取存放元素的数组
Object[] var1 = this.items;
// 通过记录的取出下标索引取出元素
Object var2 = var1[this.takeIndex];
var1[this.takeIndex] = null;
// 存放元素的数组已经到达的最后,没有元素了
if (++this.takeIndex == var1.length) {
this.takeIndex = 0;
}
// 记录元素的个数减1
--this.count;
if (this.itrs != null) {
this.itrs.elementDequeued();
}
// 唤醒所有的未满的等待队列中的元素
this.notFull.signal();
return var2;
}
参考
< JDK 1.8.0_161 源码 >