J.U.C之ArrayBlockingQueue源码解读

前言

很多并发需求需要阻塞队列去实现,比如秒杀,将请求缓存到队列里面然后mq消费等等。Today we will take about ArrayBlockingQueue.

ArrayBlockingQueue

从字面意思:阻塞队列,在队列为空和满的时候都会阻塞

源码遨游

具体方法

我们可以看到大概方法有offer,put,take,poll等等

阻塞重要变量

/** Main lock guarding all access */
    final ReentrantLock lock;

    /** Condition for waiting takes */
    private final Condition notEmpty;

    /** Condition for waiting puts */
    private final Condition notFull;

lock为锁,控制并发的。
notEmpty,当队列为空的时候阻塞对象
notFull,当队列满的时候阻塞对象
课外充电:Condition则为lock生成Condition,用来代替Object(当Object调用wait等等方法时,需要获得资源的监视器,而Condition不用,但是需要搭配ReentranLock一起使用)

take

获得value方法

public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

流程:先锁公共的锁,跟内部并发有关系。如果队列为空,一直阻塞notEmpty Condition,直到队列有值。
至于上面为啥使用lockInterruptibly,而不是lock,请看下节分解~

lockInterruptibly

take是无线循环地等待状态,如果该线程被打断的时候,它会唤醒该线程进行处理InterruptedException异常。我们可以打断线程,不让它一直等待。

poll

public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : dequeue();
        } finally {
            lock.unlock();
        }
    }

可以看到这里是不会等待或者阻塞的。

poll(long timeout, TimeUnit unit)

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0) {
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

notEmpty.awaitNanos(nanos);带时间,多长时间获取值。

获取方法小结

take方法会阻塞,offer有带时间的参数。
如果说需求没有要阻塞的话,尽量使用poll带参数的方法,不然线程一直在那里等待。
不过有个场景还是挺适合的,就是把请求扔到队列中,我们可以阻塞到它有值进行接下来的操作,而不用一直去定时查看。这个也是BlockingQueue本来的想法,不是吗?

阻塞到有值

put

添加元素

public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

这里使用notFull进行阻塞,等待。直到队列数量不等于最大数量咯~

offer

public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {
        checkNotNull(e);
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length) {
                if (nanos <= 0)
                    return false;
                nanos = notFull.awaitNanos(nanos);
            }
            enqueue(e);
            return true;
        } finally {
            lock.unlock();
        }
    }

这是一个带有时间的设置值的方法。

参考博客

发布了212 篇原创文章 · 获赞 30 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/weixin_38336658/article/details/102605028