ReentrantLock 源码解析

ReentrantLock 源码解析


前言

  • ReentrantLock 实现了 Lock 接口,内部基于 AQS 实现。所以想要弄懂 ReentrantLock ,一定要先弄明白 AQS

  • ReentrantLock 是可重入锁,同一个线程能够多次获取到同一个 ReentrantLock 锁。

  • ReentrantLock 类采用的是 AQS 技术中的独占模式,实现了 AQS 的 3 个关键方法:tryAcquire(),tryRelease(),isHeldExclusively()方法。

    • 一个线程可以多次通过 ReentrantLock 的 lock()方法获取锁,但是要想完全释放锁,就必须调用相同次数的 unlock()方法。
    • ReentrantLock 对同一个线程的可重入性是通过内部的计数器实现的,同一个线程调用lock()方法加锁,计数器就会 + 1。
    • 同一个线程调用 unlock()方法释放锁,计数器就会 - 1。
  • ReentrantLock 的内部,有两个抽象类:NonFairSync 和 FairSync,二者都继承了 Sync 类,而 Sync 类继承了 AQS 。

    • NonFairSync :表示的是非公平模式。
    • FairSync:表示的是公平模式。
  • ReentrantLock 和 AQS 的关系图:

    在这里插入图片描述

一、字段分析

  • private final Sync sync:ReentrantLock 只有这一个字段,代表了公平锁或者非公平锁,是 AQS 的子类,Sync 是ReentrantLock 内部真正用来操作的类。可以想象 ReentrantLock 只是一个壳,所有关于ReentrantLock 的操作,其实在 ReentrantLock 内部都是对 sync 进行操作的。

二、内部类分析

1、Sync

  • state:继承自 AQS,在 ReentrantLock 中的含义为:
    • 0:当前锁没有被任何线程持有。
    • 1:当一个线程第一次获取该锁时,会尝试使用CAS设置state的值为1。
    • > 1:当一个线程获取锁后,再次调用 lock(),每次调用 state + 1。每次调用 unlock()state - 1,当减为 0 时,该线程释放了锁。
abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        //抽象的获取锁的方法,由子类实现
        abstract void lock();

        //非公平锁的获取锁方法
        //acquires:传入的是 state 值,传入的是 1
        final boolean nonfairTryAcquire(int acquires) {
        	//获取当前线程
            final Thread current = Thread.currentThread();
            //获取状态值
            int c = getState();
            //状态值 = 0 ,说明当前锁没有被任何线程持有
            if (c == 0) {
            	//使用 CAS 尝试将 state 设为 1
                if (compareAndSetState(0, acquires)) {
                    //设置成功后,表示获取到了锁,将 锁持有的线程设置为当前线程
                    setExclusiveOwnerThread(current);
                    //加锁成功
                    return true;
                }
            }
            //如果 state = 0,说明已经有线程持有锁了,判断是否是当前线程对象自己持有的
            else if (current == getExclusiveOwnerThread()) {
            	//当前线程已经持有锁了,将 state + 1
                int nextc = c + acquires;
                //state 不停地 + 1,导致溢出了。重入次数过多
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //设置state
                setState(nextc);
                //可重入获取锁成功
                return true;
            }
            //锁已经被其他线程持有,当前线程获取锁失败
            return false;
        }
		
		//释放锁
		//releases :传入的是 state,ReentranLock 释放锁传入的是 1
        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);
            }
            //更新状态值
            setState(c);
            //返回是否释放成功
            return free;
        }

		//判断持有锁的线程是否为当前线程
		//true:持有锁的线程是当前线程
		//false:持有锁的线程不是当前线程
        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();
        }

        
		//获取持有锁的线程
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

		//如果持有锁的线程为当前线程,返回 state,否则返回 0
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
		
		//判断锁是否空闲
		//true:空闲
		//false:已被获取
        final boolean isLocked() {
            return getState() != 0;
        }
    }

2、FairSync

  • 公平锁的实现。
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();
            //如果状态值为0,说明锁是空闲的,可以被获取
            if (c == 0) {
            	//hasQueuedPredecessors():判断当前线程所在的节点(同步队列中)是否还有前驱节点
            	//true:有,false:同步队列为空(还未创建同步队列获只有空的head节点)或当前节点为头结点(指的是head的next节点)
            	//所以翻译为:没有前驱节点 且 使用cas方式设置 state 为 1,说明获取锁成功
            	//因为如果有前驱节点,由于是公平锁,需要先加入到同步队列的队尾进行排队,下一个尝试获取资源的是头结点代表的资源
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    //获取锁资源成功,设置锁拥有的线程为当前线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //否则判断锁拥有的线程是否为当前线程,true说明是进行锁的重入操作,将 state + 1
            else if (current == getExclusiveOwnerThread()) {
            	//计算 state,就是  state + 1
                int nextc = c + acquires;
                //判断是否溢出
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                //更新state
                setState(nextc);
                return true;
            }
            //否则获取锁失败
            return false;
        }
    }

3、NonfairSync

  • 非公平锁的实现。

    static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
    
            //获取锁
            final void lock() {
            	//使用cas的方式将state设置为1,成功说明锁未被持有,直接获取锁成功
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                	//失败说明锁已经被持有,使用非公平的方式尝试获取锁
                    acquire(1);
            }
    		//尝试获取锁资源,采用的是非公平的方式
            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
        }
    

三、方法分析

1、构造方法

	//无参构造函数,默认使用的是非公平锁
	public ReentrantLock() {
        sync = new NonfairSync();
    }

    //有参构造函数
    //true:公平锁
    //false:非公平锁
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

2、其他方法

//获取锁,调用的其实是 sync 的方法(忽视中断)
public void lock() {
        sync.lock();
    }

//获取锁,调用的其实是 sync 的方法(不忽视中断)
public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

//尝试获取锁(非公平锁)
public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

//尝试获取锁,获取失败后进入 doAcquireNanos 方法,如果时间超过了 timeout,则不进入同步队列了
public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

//释放锁,调用的其实是 sync 的方法(忽视中断)
public void unlock() {
        sync.release(1);
    }

//创建 Condition ,其实调用的是sync的方法,创建是 AQS 的 ConditionObject 对象
public Condition newCondition() {
        return sync.newCondition();
    }

//获取state,如果锁是空闲的则返回 0
public int getHoldCount() {
        return sync.getHoldCount();
    }

//true:获取到锁的线程为当前线程
//false:获取到锁的线程不为当前线程
public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }

//true:锁已被获取
//false:锁空闲
public boolean isLocked() {
        return sync.isLocked();
    }
    
//方法都比较简单
...

总结

  • ReentrantLock 的代码比较简单,更多的实现在 AQS 中,结合 ReentrantLock 和 AQS 的源码,总结下ReentrantLock 获取锁和释放锁的整个流程。

  • 公平模式下获取锁:lock()
    在这里插入图片描述

  • 非公平模式下获取锁:lock()

在这里插入图片描述

  • 非公平模式下释放锁:unlock()和 公平模式下释放锁:unlock()执行方法一样的。

在这里插入图片描述

公平模式和非公平模式下获取锁的区别:体现在 lock 方法和 tryAcquire方法

  • 公平模式下源码:

    在这里插入图片描述
    在这里插入图片描述

  • 非公平模式下源码:

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 可以发现,非公平锁模式下,如果锁是空闲状态可直接获取锁(lock方法中)。而公平方法中没有。并且在获取锁失败后,调用 tryAcquire 方法时,检查 state == 0 (说明锁是空闲的),非公平模式下是直接尝试获取锁,而公平模式是检查同步队列中是否还有节点,有的话则不获取锁,没有才获取锁。

  • 9
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值