ArrayBlockingQueue数组队列的实现原理:
1、对一个数组进行添加和取出数据操作。
2、其中的put和get用同一把lock锁进行互斥操作,控制多线程并发情况。
3、当put方法中,数组满时,通过lock下的conditionA 调用await方法阻塞当前线程(LockSupport.park方法)并释放了lock锁,并将阻塞线程存入到队列中。
4、由于3步骤已经释放了lock锁,所以新线程调用put方法时,同样会卡在数组满的这一步,与步骤3操作一样。
5、由于步骤4已经释放了lock锁,所以如有新线程调用take方法时,只要数据不是空的,就会在返回数据前执行conditionA.signal,(这里为啥不是调用signalAll,因为唤醒多个线程都去执行insert操作,数据会混乱),这样就会从aqs队列中找出conditionA操作阻塞的线程,并唤醒一个。卡在3,4步骤的其中一个线程被唤醒,则执行后续插入数据的步骤。最后执行conditionB.signal,唤醒conditionB阻塞的线程队列(若有)
6、当get方法时,数组是空的,通过lock下的conditionB,调用await方法阻塞当前线程(LockSupport.park方法)并释放了lock锁,并将阻塞线程存入到队列中。此时就只有等待put方法中执行conditionB.signal来唤醒。
如下两个例子
1、通过Lock+condition实现的:
public class LockArrayQueue<E>{
private Object[] nodes;
private int takeIndex;
private int putIndex;
private int count;
private ReentrantLock lock;
private Condition fullCondition;
private Condition emptyCondition;
public LockArrayQueue(int size){
nodes = new Object[size];
lock = new ReentrantLock(false);
fullCondition = lock.newCondition();
emptyCondition = lock.newCondition();
}
public void put(E data){
try {
lock.lockInterruptibly();
while (count == nodes.length){
try {
System.out.println(Thread.currentThread().getName()+"-put,阻塞!");
fullCondition.await();
System.out.println(Thread.currentThread().getName()+"-put,阻塞!--被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
nodes[putIndex] = data;
if (++putIndex == nodes.length){
putIndex = 0;
}
System.out.println(Thread.currentThread().getName()+"-put完!--通知其他等待的");
count++;
emptyCondition.signalAll();
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public E take(){
try {
lock.lockInterruptibly();
while(count == 0){
try {
System.out.println(Thread.currentThread().getName()+"-take,阻塞!");
emptyCondition.await();
System.out.println(Thread.currentThread().getName()+"-take,阻塞!--被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
E x = (E) nodes[takeIndex];
nodes[takeIndex] = null;
if (++takeIndex == nodes.length)
takeIndex = 0;
count--;
fullCondition.signalAll();
System.out.println(Thread.currentThread().getName()+"-take完!--通知其他等待的");
lock.unlock();
return x;
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
LockArrayQueue queueA = new LockArrayQueue(1);
new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+":"+ queueA.take());
}
}).start();
new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+":"+ queueA.take());
}
}).start();
new Thread(() -> {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
queueA.put(i+1000);
}
}).start();
第二种通过(不推荐使用下面这种):Object的wait,notify,执行的。
1、每次notify是唤醒jvm中等待获取锁的线程,由于jvm的锁是非公平锁,会将nodes数组满的等待线程也给唤醒了,会继续插入更多的数据进去。这里争抢锁的线程有,外部正准备进put和task方法的线程,还有jvm队列中已经从wait状态变为就绪状态的线程。
public class ObjectArrayQueue<E>{
private Object[] nodes;
private int takeIndex;
private int putIndex;
private int count;
public ObjectArrayQueue(int size){
nodes = new Object[size];
}
public void put(E data){
synchronized (this){
while (count == nodes.length){
try {
System.out.println(Thread.currentThread().getName()+"-put,阻塞!");
this.wait();
System.out.println(Thread.currentThread().getName()+"-put,阻塞!--被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
nodes[putIndex] = data;
if (++putIndex == nodes.length){
putIndex = 0;
}
System.out.println(Thread.currentThread().getName()+"-put完!--通知其他等待的");
count++;
this.notify();
}
}
public E take(){
synchronized (this){
while(count == 0){
try {
System.out.println(Thread.currentThread().getName()+"-take,阻塞!");
this.wait();
System.out.println(Thread.currentThread().getName()+"-take,阻塞!--被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
E x = (E) nodes[takeIndex];
nodes[takeIndex] = null;
if (++takeIndex == nodes.length)
takeIndex = 0;
count--;
this.notify();
System.out.println(Thread.currentThread().getName()+"-take完!--通知其他等待的");
return x;
}
}
public static void main(String[] args) {
ObjectArrayQueue queueA = new ObjectArrayQueue(1);
new Thread(() -> {
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+":"+ queueA.take());
}
}).start();
new Thread(() -> {
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+":"+ queueA.take());
}
}).start();
new Thread(() -> {
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
queueA.put(i+1000);
}
}).start();
new Thread(() -> {
for (int i = 50; i < 100; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
queueA.put(i+1000);
}
}).start();
}