ArrayBlockingQueue數組隊列的實現原理及實現一個demo

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();

    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章