ReentrantLock在JDK中的使用,以及与传统方法的对比(ArrayBlockingQueue源码分析)

先说说ReentrantLock与传统的有锁编程方法的区别,再来看ArrayBlockingQueue中是怎么使用ReentrantLock

 

1.ReentrantLock可以实现公平锁和非公平锁,synchronized只能实现非公平锁

2.ReentrantLock可以在多个Condition上进行等待,唤醒或者阻塞在一个Condition上等待的对象,synchronized只依赖wait() notify() notifyAll()很难做到

3.ReentrantLock要手动释放锁,一般是在finally中释放,synchronized在退出同步语句块时自动释放锁

4.ReentrantLock可以轮询尝试获取锁,synchronized做不到

 

下面是ArrayBlockingQueue的源码分析

1.公平锁

public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
}

如果以公平锁的方式进行访问,就是以请求插入/获取的先后次序来进行,反之以随机的顺序进行插入/获取。

 

还可以看到ArrayBlockingQueue有两个条件等待队列,notEmpty条件相当于等待从ArrayBlockingQueue取数据的一个队列,notFull条件相当于等待往ArrayBlockingQueue插入的一个队列

 

1.Condition vs wait() notify()

Condition相当于一个加强版的wait()notify(),能够明确的控制多个等待队列。比如说,要写一个简单的生产者-消费者模型,用wait()notify()是可以的,因为生产者只有两个状态:缓冲区满和缓冲区未满,缓冲区满的时候,就用wait()停止生产,缓冲区还有空间的时候,就用notify()来唤醒生产者,消费者同理也有两种状态。那如果有多种状态呢?这个时候用wait() notify()就很难实现了,这个时候Condition无疑是更好的选择,尽管这一点没有在ArrayBlockingQueue中体现(ArrayBlockingQueue相当于一个生产者消费者模型)。


以ArrayBlockingQueuetake为例,take()是阻塞的

public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            //当ArrayBlockingQueue为空时,让等待从Queue中取出的线程等待
            while (count == 0)
                notEmpty.await();
            return extract();
        } finally {
            lock.unlock();
        }
}
//实际调用的是extract()
private E extract() {
        final Object[] items = this.items;
        E x = this.<E>cast(items[takeIndex]);
        items[takeIndex] = null;
        takeIndex = inc(takeIndex);
        --count;
        //随机唤醒一个等待插入的线程
        notFull.signal();
        return x;
}

put()insert()同理

3.drainTo()

drainTo()函数实际上是对从ArrayBlockingQueue获取取多个元素的优化,避免了在重复的加锁解锁

public int drainTo(Collection<? super E> c, int maxElements) {
        checkNotNull(c);
        if (c == this)
            throw new IllegalArgumentException();
        if (maxElements <= 0)
            return 0;
        final Object[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            int i = takeIndex;
            int n = 0;
            int max = (maxElements < count) ? maxElements : count;
            while (n < max) {
                c.add(this.<E>cast(items[i]));
                items[i] = null;
                i = inc(i);
                ++n;
            }
            if (n > 0) {
                count -= n;
                takeIndex = i;
                notFull.signalAll();
            }
            return n;
        } finally {
            lock.unlock();
        }
}

总的思路就是如果想取的个数(maxElement)小于最大容量的话,就按想取的个数来取,否则取最大容量。只进行了一次加锁提高了效率




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值