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、首先肯定不能用notifyAll喚醒,因爲假如put有很多阻塞,一旦同時喚醒,都就會進行insert操作,將數據混亂。
2、如下使用notify沒毛病,但是若還有一個其他方法(如add,offer)也使用了Object.wait控制,那notify就容易將其他邏輯打亂。並且當前阻塞的也喚醒不了。
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();
}