ReentrantLock基于AQS的公平锁和非公平锁的实现区别

发布时间:2022-07-05 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了ReentrantLock基于AQS的公平锁和非公平锁的实现区别脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

ReentrantLock锁的实现是基于AQS实现的,所以先简单说下AQS:

AQS是AbstractQueuedSynchronizer缩写,顾名思义:抽象的队列同步器,它是JUC里面许多同步工具类实现的核心

其实简单来说AQS有两个核心,一个是volatile修饰的int类型state,这个是记录处于等待中需要持有锁和正在持有锁的线程数量

/**
 * The synchronization state.
 */
PRivate volatile int state;

第二个就是Node内部类,他是AQS里面FIFO双向队列节点的实现,他的一些属性如下:

static final class Node {     volatile int waitStatus;//等待状态

        volatile Node prev;//前驱节点

        volatile Node next;//后继节点

        volatile Thread thread;//申请同步状态的线程

        Node nextWaITer;//等待节点的后继节点(后续出AQS详细篇再细讲)
    }

这种结构可以从任意的一个节点开始很方便的访问前驱和后继节点。每个Node由线程封装,当线程竞争失败后会加入到AQS队列中去。

基于这些再继续聊下ReentrantLock的公平和非公平锁的实现

ReentrantLock锁的实现基于AQS,如下sync抽象内部类:

abstract static class Sync extends AbstractQueuedSynchronizer{
  abstract void lock(); 
}

Sync的子类有两个:FairSync和NonfairSync(公平和非公平锁的具体实现类),两个锁的不同就在于两个方法的实现不同lock():加锁的方法,还有tryAcquire(int acquires):尝试持有锁的方法,获取成功返回true,失败返回false

看看两把锁的码实现:

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        final void lock() {
            acquire(1);
        }
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

  

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

nonfairTryAcquire方法是在父类Sync中定义:

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

可以看见,NonfairSync在使用lock()加锁的时候就已经体现了非公平性了,因为lock()加锁的时候直接尝试使用CAS获取锁,如果获取到了就不会入等待队列,所以会有后来的线程先抢占到锁;

那如果没有抢占到锁呢?会走else的acquire(1)方法。

先看看acquire方法的定义:

public final void acquire(int arg) {
        if (!tryAcquire(arg) &amp;&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

这里会发现acquire调用了tryAcquire方法,而在上面两把锁的源码中各自重写了tryAcquire方法

这个方法中当getState()获取同步状态为0(没有线程持有锁)时候,继续使用用CAS获取锁

两个重写的方法唯一不同之处在于,公平锁加了hasQueuedPredecessors()方法:

public final boolean hasQueuedPredecessors() {
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

这个方法主要是用来判断线程需不需要排队,因为队列是FIFO的,所以需要判断队列中有没有相关线程的节点已经在排队了。有则返回true表示线程需要排队,没有则返回false则表示线程无需排队。

而非公平锁就没有判断FIFO队列是否还有排队。

小结:

ReentrantLock中公平锁和非公平锁的实现主要有两处区别:

1.在lock()方法中,非公平锁一进来就尝试CAS获取锁,不会管等待队列里面是否有等待线程

2.在tryAcquire方法中,判断state等于0(没有线程持有锁的情况)后,公平锁会先调用hasQueuedPredecessors()方法判断FIFO队列是否有等待线程,没有才继续尝试获取锁,而非公平锁是直接CAS获取锁

脚本宝典总结

以上是脚本宝典为你收集整理的ReentrantLock基于AQS的公平锁和非公平锁的实现区别全部内容,希望文章能够帮你解决ReentrantLock基于AQS的公平锁和非公平锁的实现区别所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。