并发编程学习笔记 之 ReentrantLock实现原理的探究

1、前言

  在前面我们已经了解到Lock接口,是对锁操作方法的一个基本定义,它提供了synchronized关键字所具备的全部功能方法,而ReentrantLock类不仅完全实现了显示锁Lock接口所定义的接口,也扩展了对使用显式锁Lock的一些监控方法。同时,我们也尝试使用ReentrantLock实现了对共享资源的同步访问,这一节我们来深入了解,这些功能是如何实现的。

2、ReentrantLock(重入锁)实现原理

2.1、ReentrantLock对接口方法的实现

  通过查看ReentrantLock类的源码,我们会发现所有实现Lock接口接口的方法,包括额外增加的一些监控方式,它们其实都是借助一个Sync变量实现的,如下所示:

public void lock() {
   sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
   sync.acquireInterruptibly(1);
}
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}
public void unlock() {
    sync.release(1);
}
public Condition newCondition() {
    return sync.newCondition();
}

//ReentrantLock实现的一些监控方法,同样也是借助sync实现的,代码如下(篇幅原因,仅显示了其中一个说明情况)
public int getHoldCount() {
    return sync.getHoldCount();
}
//……

  通过上述的部分源码,我们已经可以确定ReentrantLock类的实现,主要基于sync对象,只要我们把Sync对象研究清楚了,那么ReentrantLock类的原理我们自然而然的就搞清楚了,下面我们开始学习Sync类的实现。

2.2、ReentrantLock构造函数

  ReentrantLock提供了公平锁和非公平锁的机制,主要基于Sync 的实现类FairSync和NonfairSync实现。默认构造函数使用的是非公平锁的机制,可以使用带参数的构造函数,应用公平锁机制。构造函数如下:

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
public ReentrantLock() {
  sync = new NonfairSync();
}
2.2、Sync内部类

  ReentrantLock对Lock的实现都是基于Sync来做的,在前面的源码已经清楚的看到了,这里我们就重点来学习Sync类的实现。

  Sync类的层级结构:

在这里插入图片描述
  根据Sync的层级结构,我们可以知道该类继承了AbstractQueuedSynchronizer(AQS)抽象类,同时有两个实现类FairSync和NonfairSync,分别应用于公平锁和非公平锁机制。其中,AbstractQueuedSynchronizer抽象类定义了一套多线程访问共享资源的同步模板,解决了实现同步器时涉及的大量细节问题,能够极大地减少实现工作,即AbstractQueuedSynchronizer为加锁和解锁过程提供了统一的模板函数,Sync类只需要实现部分抽象方法即可。

这里暂时不深入学习AbstractQueuedSynchronizer(AQS)抽象类,后续专门一篇进行介绍。

  在Sync类中,主要实现了nonfairTryAcquire()和tryRelease()方法,当然,还有一些其他方法的实现, 具体请看源码的注释:

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

   /**
    * 抽象方法,在实现类FairSync和NonfairSync中实现,
    * 在实现类的方法中,决定了获取锁的公平锁或非公平锁的策略
    */
   abstract void lock();

   /**
    * 非公平锁-尝试获取锁资源的方法,而公平锁尝试获取资源的方法却在实现类FairSync中实现。
    */
   final boolean nonfairTryAcquire(int acquires) {
   		//获取当前线程
       final Thread current = Thread.currentThread();
       //获取state状态,表示锁状态,getState()方法在AbstractQueuedSynchronizer(AQS)抽象类中实现。
       int c = getState();
       if (c == 0) { //等于0,表示资源可用
       		//cas方法设置状态,保证原子性
           if (compareAndSetState(0, acquires)) {
           		//获取成功,设置持有锁的线程,并返回获取锁成功
               setExclusiveOwnerThread(current);
               return true;
           }
       }else if (current == getExclusiveOwnerThread()) {//判断当前线程是否持有锁,可重入特性
       		//设置state的数值
           int nextc = c + acquires;
           if (nextc < 0) // 超过上限时
               throw new Error("Maximum lock count exceeded");
               //当前线程设置,不需要cas方式
           setState(nextc);
           return true;
       }
       return false;
   }
	/**
	 * 释放锁资源
	 */
   protected final boolean tryRelease(int releases) {
   		//计算如果释放成功后,当前线程持有的hold数量
       int c = getState() - releases;
       //判断是否是当前线程释放锁,不是的话,抛出异常
       if (Thread.currentThread() != getExclusiveOwnerThread())
           throw new IllegalMonitorStateException();
       boolean free = false;
       //如果c==0,说明释放锁成功,同时把持有锁的线程设置为null
       if (c == 0) {
           free = true;
           setExclusiveOwnerThread(null);
       }
       //c==0时,free=true,释放锁成功,c!=0时,free=false,释放锁失败,说明有重入操作
       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();
   }
	/**
	 * 获取Hold数量,是当前线程持有锁,就返回state,否则(当前线程没有持有锁时),返回0
	 */
   final int getHoldCount() {
       return isHeldExclusively() ? getState() : 0;
   }
	/**
	 * 锁资源是否被占用
	 */
   final boolean isLocked() {
       return getState() != 0;
   }
   /**
	 * 反序列化
	 */
   private void readObject(java.io.ObjectInputStream s)
       throws java.io.IOException, ClassNotFoundException {
       s.defaultReadObject();
       setState(0); // reset to unlocked state
   }
}
2.3、NonfairSync实现类 (非公平锁策略)

  获取锁失败的线程,会进入CLH队列阻塞,其他线程解锁会唤醒CLH队列线程,当重新竞争锁时,如果有非CLH队列的线程(还没来得及进入CLH队列阻塞的线程)参与竞争,那么就是非公平锁策略。
  非公平锁策略实现类NonfairSync ,主要实现了Sync类的抽象方法lock(),在该方法中,实现了非公平锁策略;同时还实现了tryAcquire()方法(主要逻辑在Sync类的nonfairTryAcquire()方法中实现),用于获取,具体如下:

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * 获取锁资源,个人感觉如果发生锁竞争时,正好有线程来获取锁,是不是会比CLH阻塞队列中的线程更容易获得锁呢?
     */
    final void lock() {
        if (compareAndSetState(0, 1))//cas方法尝试设置,如果设置成功,当前线程就可以获取锁
            setExclusiveOwnerThread(Thread.currentThread());
        else
        	//在AbstractQueuedSynchronizer(AQS)抽象类中实现,执行AQS获取锁的流程
            acquire(1);
    }
	/**
     * 尝试获取锁资源,在Syns类中定义nonfairTryAcquire()方法
     */
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}
2.4、FairSync实现类 (公平锁策略)

  FairSync流程与NonfairSync类似,都是实现了lock()和tryAcquire()方法,区别在于:

  1. lock() 方法实现中,在FairSync中直接调用了acquire(1)方法,而在NonfairSync类中,首先进行了cas操作,然后失败后才调用了acquire(1)方法。
  2. 在tryAcquire()方法中,FairSync类在自己内部实现逻辑,且在在cas操作前,多了一步hasQueuedPredecessors函数,验证是否在CLH阻塞队列,而NonfairSync类中,则是调用了nonfairTryAcquire()方法,且不需要验证是否在CLH阻塞队列。
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
    	//直接调用AbstractQueuedSynchronizer(AQS)抽象类中acquire(),执行AQS获取锁的流程
        acquire(1);
    }
	/**
	 * 获取锁资源
	 */
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
        	//hasQueuedPredecessors()方法在AQS抽象类中定义,验证当前现场是否在CLH阻塞队列中,不在队列中,则获取锁失败,同时还需要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");
            setState(nextc);
            return true;
        }
        return false;
    }
}

3、总结

  至此,关于ReentrantLock的实现原理我们就学习完了,但是其中涉及到的CLH阻塞队列、AQS抽象类中的hasQueuedPredecessors()、acquire()等方法,我们都没有深入学习,我们计划在下一节中,专门深入研究一下AQS的实现原理,敬请期待!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姠惢荇者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值