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、每次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();

    }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值