ReentrantLock(重入锁)简单源码分析

Posted ql211lin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReentrantLock(重入锁)简单源码分析相关的知识,希望对你有一定的参考价值。

1.ReentrantLock是基于AQS实现的一种重入锁。

2.先介绍下公平锁/非公平锁

公平锁

  • 公平锁是指多个线程按照申请锁的顺序来获取锁。

非公平锁

  • 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。

3.重入锁/不可重入锁

可重入锁:广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。

不可重入锁:不可重入锁,与可重入锁相反,不可递归调用,递归调用就发生死锁。

4.统一入口

 //获取锁。
    @Override
    public void lock() {
        sync.lock();
    }


    //获取锁,响应中断
    @Override
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    //仅在调用时锁未被另一个线程保持的情况下,才获取该锁。
    @Override
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }


    //如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被 中断,则获取该锁。
    @Override
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    //试图释放此锁。
    @Override
    public void unlock() {
        sync.release(1);
    }

ReentrantLock默认是非公平模式,可以通过构造函数指定模式。

/**
     * 构造函数,默认非公平锁
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * 构造函数,通过布尔参数设置是否是公平锁
     * @param fair
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

4.非公平模式的加锁和解锁

 加锁:

//获取锁。
    @Override
    public void lock() {
        sync.lock();
    }

然后调用NonfairSync的lock方法:

/**
         * 加锁
         */
        @Override
        final void lock() {
            //以cas方式尝试将AQS中的state从0更新为1
            if (compareAndSetState(0, 1)) {
                //获取锁成功则将当前线程标记为持有锁的线程,然后直接返回
                setExclusiveOwnerThread(Thread.currentThread());
            } else {
                acquire(1);
            }
        }

如果设置state失败,则调用NonfairSync的tryAcquire方法

@Override
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

然后调用Sync的nonfairTryAcquire方法:

/**
         * 非公平模式的情况下获取同步状态
         * @param acquires
         * @return
         */
        final boolean nonfairTryAcquire(int acquires) {
            //获取当前线程实例
            final Thread current = Thread.currentThread();
            //获取同步state变量的值,即当前锁被重入的次数
            int c = getState();
            //state为0,说明当前锁未被任何线程持有
            if (c == 0) {
                //以cas方式获取锁
                if (compareAndSetState(0, acquires)) {
                    //将当前线程标记为持有锁的线程
                    setExclusiveOwnerThread(current);
                    //获取锁成功,非重入
                    return true;
                }
                //当前线程就是持有锁的线程,说明该锁被重入了,实现重入
            } else if (current == getExclusiveOwnerThread()) {
                //计算state变量要更新的值
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //非同步方式更新state值
                setState(nextc);
                //获取锁成功,重入
                return true;
            }
            //尝试获取锁失败
            return false;
        }

这边体现了锁的重入,用state的次数表示锁被线程重入了state次。

else if (current == getExclusiveOwnerThread()) {
                //计算state变量要更新的值
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //非同步方式更新state值
                setState(nextc);
                //获取锁成功,重入
                return true;
            }

 解锁

//试图释放此锁。
    @Override
    public void unlock() {
        sync.release(1);
    }

然后调用Sync的tryRelease方法:

/**
         * 释放同步状态
         * @param releases
         * @return
         */
        @Override
        protected final boolean tryRelease(int releases) {
            //计算待更新的state值
            int c = getState() - releases;
            //判断当前线程是否独占
            if (Thread.currentThread() != getExclusiveOwnerThread()) {
                throw new IllegalMonitorStateException();
            }
            //是否释放同步状态成功的标志
            boolean free = false;
            //待更新的state值为0,说明持有锁的线程未重入,一旦释放锁其他线程将能获取
            if (c == 0) {
                free = true;
                //清除锁的持有线程标记
                setExclusiveOwnerThread(null);
            }
            //更新state值
            setState(c);
            return free;
        }
 锁的最终释放要去锁对于获取进行计数自增,计算表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数为0时表示已经释放成功。

5.公平模式的加锁和解锁

 加锁:

 

@Override
        protected final boolean tryAcquire(int acquires) {
            //获取当前线程实例
            final Thread current = Thread.currentThread();
            //获取同步state变量的值,即当前锁被重入的次数
            int c = getState();
            //state为0,说明当前锁未被任何线程持有
            if (c == 0) {
                /**
                 * 1.判断同步队列中当前节点是否有前驱节点
                 * 2. 以cas方式获取锁
                 */
                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");
                //非同步方式更新state值
                setState(nextc);
                //获取锁成功,重入
                return true;
            }
            //尝试获取锁失败
            return false;
        }

 

对于非公平锁,只要cas设置同步状态成功,则表示当前线程获取了锁,而公平锁则不同,公平锁的获取增加了hasQueuedPredecessors的判断,即加入了判断同步队列中当前节点是否有前驱节点,

如果返回true,则表示有线程比当前线程更早滴请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。

  解锁:同公平锁的解锁方式

 

 

 

以上是关于ReentrantLock(重入锁)简单源码分析的主要内容,如果未能解决你的问题,请参考以下文章

ReentrantLock核心源码分析,AQS独占模式,可重入锁

ReentrantLock (重入锁) 源码浅析

Java并发-- ReentrantLock 可重入锁实现原理1 - 获取锁

Java并发编程之ReentrantLock源码分析

JUC源码学习02重入锁(ReentrantLock)学习

Java 重入锁 ReentrantLock 原理分析