从源码角度理解ReentrantLock(java.util.concurrent.locks.ReentrantLock)

一、ReentrantLock介绍

ReentrantLock重入锁,能够实现对同一个资源的重复加锁,即当前线程重复申请资源的时候,在已经拥有锁的前提下,不用被阻塞,便可以直接拥有锁。对于ReentrantLock的理解主要是理解如何实现重入性,公平锁和非公平锁对Sync内部类的实现。我们在这里主要讲解公平锁和非公平锁,重入性的实现在讲解的时候指出。
在使用的时候,官方建议一直跟随try代码块,例如

public void m() {
   lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
        lock.unlock()
     }
 }

这样可以避免因为出现异常等原因无法释放锁,导致其他线程一直无法获取锁。

二、ReentrantLock继承关系

ReentrantLock的继承关系及其内部类
该图显示了ReentrantLock的继承关系以及内部实现类。
ReentrantLock实现了Lock接口,表明该锁是JDK层面的锁,而不是JVM层面的锁(synchronized),也可以实现对资源进行加锁,解锁等操作。
其中FairSync是公平锁的实现类,NonfairSync是非公平锁的实现类。Sync继承自AbstractQueuedSynchronizer抽象类。

三、Sync

继承自AbstractQueuedSynchronizer,简称AQS,他提供了一个机遇FIFO的队列,可以用于实现依赖于先进先出(FIFO)等待队列的阻塞锁锁和相关同步器等。

3.1 主要实现的方法

加锁操作

		/**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         *
         * 执行非公平的tryLock,tryAcquire继承自子类,
         * 但是两个都需要非公平的trylock方法
         *
         */
        final boolean nonfairTryAcquire(int acquires) {       // 传入请求资源的个数
            final Thread current = Thread.currentThread();   // 获得当前线程
            int c = getState();   //  获得锁的状态
            if (c == 0) {            // 当前没有被锁定
                if (compareAndSetState(0, acquires)) {    // 尝试获取锁
                    setExclusiveOwnerThread(current);     //  设置当前拥有独占访问权限的线程
                    return true;     // 加锁成功,返回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);     // 将拥有独占权限的线程设置为null,表示没有线程拥有独占访问权限
            }
            setState(c);     // 更新锁状态
            return free;
        }

里面的state成员变量在AbstractQueuedSynchronizer抽象类中,表示的是同步的状态,数值为多少就表示有多少的锁。

四、FairSync(公平锁)

4.1 加锁

加锁时直接调用的是lock()方法,lock()方法通过acquire()方法间接的调用tryAcquire()方法进行加锁。

		/**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();   // 获得当前线程
            int c = getState();     // 获得当前的锁状态
            if (c == 0) {  // 当前锁为空
                if (!hasQueuedPredecessors() &&   // 判断是否有比当前线程等待更久的线程
                        compareAndSetState(0, acquires)) {     // 如果没有就使用CAS算法,修改锁状态
                    setExclusiveOwnerThread(current);         // 设置拥有独占访问权限的线程是当前线程
                    return true;    //  加锁成功,返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;                              //  加锁失败
        }

在没有锁的情况下使用CAS算法进行修改,而在有锁的情况下直接修改便可,是因为在有锁的情况下其他线程无法访问并修改状态,而在无锁状态下,其他线程也可以访问并修改锁的状态,所以必须使用CAS算法。

4.2 加锁失败,进入等待

当在加锁的时候,会调用java.util.concurrent.locks.AbstractQueuedSynchronizer中的acquire()方法

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&      // 先尝试加锁
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))   //  如果加锁失败便将该线程加入到等待队列
            selfInterrupt();   // 两者均失败,便终止该线程
    }

4.3 释放锁

锁的释放使用的是Sync类里面的tryRelease()方法,与上述的释放锁的方式一致。

五、非公平锁

5.1 加锁

final void lock() {
     if (compareAndSetState(0, 1))   // 使用CAS算法判断当前是否已经被锁住(如果成功交换就代表没有被锁住)
        setExclusiveOwnerThread(Thread.currentThread());  // 就设置拥有独占权限的线程是当前线程
     else
        acquire(1);    // 将会重新尝试加锁,如果依旧加锁失败,便加入等待队列,然后中断当前线程
 }

非公平锁在第一步加锁失败,重新尝试加锁的时候,会调用Sync中的nonfairTryAcquire()方法,该方法上面已经讲述过,便不在赘述。

5.2 释放锁

锁的释放使用的是Sync类里面的tryRelease()方法

六、其他方法

其他方法均是获得一些基本信息,直接阅读就可以看懂

如果文章中有任何错误的地方,欢迎指出,谢谢!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值