ReenTrantLock源码浅析

ReenTrantLock是juc包下的一个经典的互斥,也是 可重入锁 (即当前线程在已经获取改锁后重复执行获取锁操作时不会引起死锁,并且不需要执行获取锁的操作),ReenTrantLock是基于AQS来实现的(PS:注意是基于不是继承,看到网上有的同学会说发现ReentrantLock并没有继承AQS有所疑问),如果有对AQS不了解的同学可以查看我之前的文章 浅析AQS(1)—独占锁以及共享锁的实现 以及 浅析AQS (二)–condition的实现 ,

首先ReenTrantLock是默认的非公平锁,但是也有公平锁的实现,需要在构造方法中传入是否公平的Boolean值来标志,需要注意的是,此处的公平与非公平只针对lock方法,而tryLock方法是指定非公平的.并不受限制

ReenTrantLock实现并发控制是依靠内部类Sync及其两个子类FairSync和NoFairSync,顾名思义这两个子类是提供公平锁以及非公平锁的实现,而Sync则是继承了AQS的具体实现类,接下来我们结合源码具体分析

Sync内部类

首先查看一下Sync的源码

abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

    	//抽象lock方法 根据公平与非公平实现执行不同的操作
        abstract void lock();
		//非公平尝试获取锁
        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;
        }
		//尝试释放锁
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
		// 是否持有当前锁
        protected final boolean isHeldExclusively() {
             return getExclusiveOwnerThread() == Thread.currentThread();
        }
		//新建condition对象
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
		//获取锁当前的持有线程
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
		//当前线程获取到了
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
        final boolean isLocked() {
            return getState() != 0;
        }
    }
复制代码

在Sync对象中,重写了tryRelease方法,将释放锁的逻辑统一,并且定义了nonfairTryAcquire非公平获取锁的方法,这里为什么要将该方法定义在父类是因为无论是公平还是非公平锁,tryLock方法中都是采用非公平获取的方式,而无论维护的Sync对象时公平的还是非公平的,我们都需要一个可以非公平获取锁的方式

在nonfairTryAcquire我们可以看到,如果当前锁的state为0即当前锁没人持有时则采用cas的方式获取锁,然后将持有现成设置为当前线程,而第二个判断则是可重入锁的实现,即判断持有锁的是不是当前线程,如果是则直接获取锁

关于ConditionObject的实现可以查看我以前的文章 浅析AQS (二)–condition的实现 ,

FairSync与NoFairSync

下面放一下公平与非公平锁的具体实现

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


    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;
        }
    
复制代码

在NoFairSync中调用lock方法直接尝试用cas的方式获取锁,并不去查看你是否有锁占用,直接尝试获取,如果成功则直接获取锁,如果失败则采用acquire的方式来获取

而在FairSync中实现的tryAcquire方法中与Sync类中的noFairTryAcquire基本一致,只是在没人获取锁时判断当前队列是否为空,或者当前线程是否在队列的最开始位置,总得来说就是判断是否有其他线程等待时长比当前线程要长,主要是为了保证获取锁的公平性

ReenTrantLock

接下来我们看看ReenTrantLock是如何使用这两种锁的,首先来看看构造方法

public ReentrantLock() {
        sync = new NonfairSync();
    }


    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
复制代码

这里指定了锁的公平与非公平,默认采用非公平锁,如需公平锁则传入一个true即可

接下来查看lock与tryLock方法

public void lock() {
        sync.lock();
    }

    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

复制代码

如同上文所说,在lock方法中是根据当前所公平与非公平来进行而tryLock方法则是固定以非公平的方式来进行调用

以上就是ReenTrantLock的具体实现方式,结合AQS的两篇文章即可将该类理解通透

ReenTrantLock源码浅析

原文 

https://juejin.im/post/5f0eb099f265da22f955ac2b

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » ReenTrantLock源码浅析

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址