前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java源码之ReentrantLock

Java源码之ReentrantLock

作者头像
每天学Java
发布2020-06-02 09:53:38
2780
发布2020-06-02 09:53:38
举报
文章被收录于专栏:每天学Java每天学Java

上一篇文章分析了锁框架的AQS的源码,今天我们来分析一种具体的锁:重入锁ReentrantLock的源码,前面我们也说到ReentrantLock内部最重要的实现是基于AQS做的

在分析重入锁代码之前,我们需要先知道重入锁ReentrantLock分为公平锁和非公平锁(通过构造函数创建不同的锁),它的内部基于AQS实现抽象类Sync同步器(在AQS文章中我们谈论过)。

01

首先我们看一下ReentrantLock的内部抽象类Sync。Sync在ReentrantLock中承担着基石的作用,在它的内部实现一系列的方法用于ReentrantLock定义公平锁和非公锁,代码中注释关于Sync说的很清楚了(自认为)。

代码语言:javascript
复制
abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         * 非公平锁获取
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
         //getState()是AQS的方法,用于返回锁状态
            int c = getState();
         //如果c==0说明锁没有被占有
            if (c == 0) {
         //开始比较和更新state的状态
                if (compareAndSetState(0, acquires)) {
         //设置锁的拥有者为当前锁,setExclusiveOwnerThread是AbstractOwnableSynchronizer的方法
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
         //如果c!=0,判断锁是否是当前线程拥有
            else if (current == getExclusiveOwnerThread()) {
         //既然锁的拥有者是当前线程,那么c+1,这样释放锁的时候,不会立刻释放,锁的持有者仍是当前线程,类似与偏向锁
                int nextc = c + acquires;
         //超过最大锁计数
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
         //更新state
                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;
         //c==0,说明这个线程获取锁一次,此时将锁的拥有者设为null,因为如果获取两次,c必然大于0,上面nonfairTryAcquire方法中同一线程多次获取锁,会增加
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
         //c!=0 更新c的值,返回false
            setState(c);
            return free;
        }
         // 是否占有锁 
        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
         //关于ConditionObject这里先不说
        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class
         //获取拥有锁的线程
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
         //如果拥有锁返回持有数量,否则返回0
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
         //是否上锁,getState!=0的情况表明已经上锁
        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).反序列化
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

关于Sync上面我们已经了解过,现在我们再来看一下基于Sync实现的公平锁FairSync和非公平锁NonfairSync

代码语言:javascript
复制
    //非公平锁
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        //lock是Sync中已经定义好
        final void lock() {
        //首先判断锁是否被获取,没有的化更新state的状态
            if (compareAndSetState(0, 1))
        //设置锁的拥有者为当前线程
                setExclusiveOwnerThread(Thread.currentThread());
            else
        //被获取的情况下,调用AQS的acquire方法,这个方法前面我们已经分析过:
        //首先获取锁,失败的情况下调用addWaiter,将该线程存放在节点中,等待被执行
        //trrAcquire已被重写,实际上调用nonfairTryAcquire方法
                acquire(1);
        }
        //nonfairTryAcquire方法在上面Sync中我们已经分析过
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

公平锁:

代码语言:javascript
复制
      static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            //不同于非公平锁,这里直接使用acquire方法,走AQS的业务逻辑
            //但是AQS中的tryAcquire方法被重写。
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        //重写AQS方法
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            //跟非公平锁不同的是这里加上了hasQueuedPredecessors
            //用于判断是否该当前线程执行,满足公平的条件
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //下面代码跟Sync中作用一样
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

既然基于AQS的同步器Sync以及公平锁和非公平锁代码我们已经分析过(我觉得核心还是要去分析AQS的代码,然后才能理解重入锁代码逻辑)。下面我们再来看一下ReentrantLock的方法:

首先是它的两个构造方法

代码语言:javascript
复制
   //无参构造方法,默认非公平锁
    public ReentrantLock() {
        sync = new NonfairSync();
    }
   //带参构造方法,根据参数实现公平锁或者非公平锁
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

然后是lock方法

代码语言:javascript
复制
    //sync在开始已被定义,何种锁也在构造方法中确定,这里直接调用Sync实现类的lock方法
    public void lock() {
        sync.lock();
    }

既然有lock方法那必然有unlock方法

代码语言:javascript
复制
    //释放锁
    public void unlock() {
        sync.release(1);
    }

lock和unlock是我们常用的两个方法,但是ReentrantLock还存在其他的方法,我们大致的来看一下:

lockInterruptibly

代码语言:javascript
复制
     //在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,
     //这时不用获取锁,而会抛出一个InterruptedException
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

tryLock

代码语言:javascript
复制
   //以非公平锁的形式尝试获取锁,这也说明了为什么nonfairTryAcquire定义在Sync中,以及他叫可重入锁
   public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

tryLock(long timeout, TimeUnit unit)

代码语言:javascript
复制
    //试图以独占模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

newCondition

代码语言:javascript
复制
    public Condition newCondition() {
        return sync.newCondition();
    }

getHoldCount :获取当前线程持有锁次数

代码语言:javascript
复制
    public int getHoldCount() {
        return sync.getHoldCount();
    }

isHeldByCurrentThread:是否是当前线程占有锁

代码语言:javascript
复制
    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }

isLocked:锁是否被占有

代码语言:javascript
复制
    public boolean isLocked() {
        return sync.isLocked();
    }

isFair:是否公平锁

代码语言:javascript
复制
    public final boolean isFair() {
        return sync instanceof FairSync;
    }

getOwner:获取持有锁的线程

代码语言:javascript
复制
    protected Thread getOwner() {
        return sync.getOwner();
    }

hasQueuedThreads:用于查询是否有线程正在等待获取指定的对象监视器

代码语言:javascript
复制
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }

hasQueuedThread(Thread thread) :用来查询指定的线程是否正在等待获取指定的对象监视器

代码语言:javascript
复制
    public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }

getQueueLength()方法用于获取正等待获取此锁定的线程估计数。注意"估计"两个字,因为此方法遍历内部数据结构的同时,线程的数据可能动态变化

代码语言:javascript
复制
    public final int getQueueLength() {
        return sync.getQueueLength();
    }

getQueuedThreads:返回一个 collection,包含可能等待获取的线程。因为在构造此结果的同时实际的线程 set 可能动态地变化,所以返回的 collection 仅是尽力的估计值

代码语言:javascript
复制
    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }

hasWaiters:查询是否有线程正在等待与此锁有关的condition条件

代码语言:javascript
复制
   public boolean hasWaiters(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

getWaitQueueLength:类似getQueueLength(),不过此方法的前提是condition

代码语言:javascript
复制
 public int getWaitQueueLength(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

getWaitingThreads:返回一个collection,它包含可能正在等待与此锁相关给定条件的那些线程,因为构造结果的时候实际线程可能动态变化,因此返回的collection只是尽力的估计值

代码语言:javascript
复制
 protected Collection<Thread> getWaitingThreads(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
    }
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 每天学Java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档