概念

ReentrantLock,可重入锁。在多线程中,可以通过加锁保证线程安全。

加锁和解锁

  • 加锁:
public void lock() {
sync.lock();
}
  • 解锁
public void unlock() {
sync.release(1);
}

内部类Sync继承AQS(AbstractQueuedSynchronizer),因此可以维护状态变量state,通过acquire()获取state、release()释放state。后文会涉及。

构造方法

  • 无参构造方法

默认使用非公平锁。

/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
  • 有参构造方法

根据参数判断是否公平锁。

/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

Sync类

Sync类是一个继承AQS的内部静态抽象类。

Sync继承AQS,因此也可以维护状态变量state,通过acquire()获取state、release()释放state。

公平锁FairSync和非公平锁NonfairSync,都继承Sync类,并且重写了Sync类中的抽象方法Lock()。

其中的nonfairTryAcquire()方法,是ReentrantLock最核心的实现。

如果状态变量state为0,说明目前还没有其他线程加锁,当前线程可以进行加锁,通过CAS将状态变量state加1。

并且当前线程会获得锁。

如果状态变量state不为0,且获取锁的是当前线程,则将state加1。

    /**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L; /**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
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();
int c = getState();
//如果状态变量state为0,说明目前还没有其他线程加锁,当前线程可以进行加锁,通过CAS将状态变量state加1。
//并且当前线程会获得锁。
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果状态变量state不为0,且获取锁的是当前线程,则将state加1。
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;
} //释放state
protected final boolean tryRelease(int releases) {
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;
} 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();
} final ConditionObject newCondition() {
return new ConditionObject();
} // Methods relayed from outer class final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
} final int getHoldCount() {
return isHeldExclusively() ? 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
}
}

非公平锁和公平锁

非公平锁和公平锁,都重写了tryAcquire()。而acquire()是AQS中的方法。

  • 非公平锁:

非公平锁的调用过程: lock()---> acquire(1) ---> tryAcquire(1) ---> nonfairTryAcquire(1)

/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L; /**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
} protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
  • 公平锁:

主要流程:lock()--->acquire(1)--->tryAcquire(1)---> hasQueuedPredecessors()

公平锁和非公平锁的主要区别是:

公平锁,会去判断“当前线程”是不是AQS的线程等待队列(CLH队列)中的第一个线程,并对state进行CAS操作。

这个区别在hasQueuedPredecessors()方法中体现。

    /**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L; final void lock() {
acquire(1);
} /**
* 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();
//如果当前状态变量为0(也就是没有线程获取到锁)
if (c == 0) {
//判断“当前线程”是不是AQS的线程等待队列(CLH队列)中的第一个线程,并对state进行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;
}
}

ReentrantLock和Synchronized的区别

  • Synchronized是基于监视锁monitor实现的,当monitor为0时,线程能够获得锁,此时monitor加1。当有线程进行请求时,判断monitor是否为0,如果不为0,说明锁已经被其他线程拿到了。Synchronized是JVM级别的锁。

而ReentrantLock是基于AQS实现的,AQS是一个抽象队列容器。当有线程发起请求时,如果是公平锁,需要在队列中排除等待。如果是非公平锁,会插队。

  • Synchronized可以是方法锁、对象锁。

而ReentrantLock可以在代码中灵活地加锁、解锁。但要注意,必须在finally中进行unlock()。

其他资料

AQS详情见: https://www.cnblogs.com/expiator/p/12052125.html

ReentrantLock源码简析的更多相关文章

  1. SpringMVC学习(一)——概念、流程图、源码简析

    学习资料:开涛的<跟我学SpringMVC.pdf> 众所周知,springMVC是比较常用的web框架,通常整合spring使用.这里抛开spring,单纯的对springMVC做一下总 ...

  2. Flink源码阅读(一)——Flink on Yarn的Per-job模式源码简析

    一.前言 个人感觉学习Flink其实最不应该错过的博文是Flink社区的博文系列,里面的文章是不会让人失望的.强烈安利:https://ververica.cn/developers-resource ...

  3. django-jwt token校验源码简析

    一. jwt token校验源码简析 1.1 前言 之前使用jwt签发了token,里面的头部包含了加密的方式.是否有签名等,而载荷中包含用户名.用户主键.过期时间等信息,最后的签名还使用了摘要算法进 ...

  4. 0002 - Spring MVC 拦截器源码简析:拦截器加载与执行

    1.概述 Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理.例如通过拦截器可以进行权限验证.记录请求信息的日 ...

  5. OpenStack之Glance源码简析

    Glance简介 OpenStack镜像服务器是一套虚拟机镜像发现.注册.检索. glance架构图: Glance源码结构: glance/api:主要负责接收响应镜像管理命令的Restful请求, ...

  6. AFNetworking源码简析

    AFNetworking基本是苹果开发中网络请求库的标配,它是一个轻量级的网络库,专门针对iOS和OS X的网络应用设计,具有模块化的架构和丰富的APIs接口,功能强大并且使用简单,深受苹果应用开发人 ...

  7. ElementUI 源码简析——源码结构篇

    ElementUI 作为当前运用的最广的 Vue PC 端组件库,很多 Vue 组件库的架构都是参照 ElementUI 做的.作为一个有梦想的前端(咸鱼),当然需要好好学习一番这套比较成熟的架构. ...

  8. DRF之APIView源码简析

    一. 安装djangorestframework 安装的方式有以下三种,注意,模块就叫djangorestframework. 方式一:pip3 install djangorestframework ...

  9. spring ioc源码简析

    ClassPathXmlApplicationContext 首先我们先从平时启动spring常用的ClassPathXmlApplicationContext开始解析 ApplicationCont ...

随机推荐

  1. 2019-11-29-WPF-开启-ScrollViewer-的触摸滚动

    原文:2019-11-29-WPF-开启-ScrollViewer-的触摸滚动 title author date CreateTime categories WPF 开启 ScrollViewer ...

  2. 49道spring面试题整理,附带答案

    1.选择使用Spring框架的原因? 使用Spring: 第一是使用它的IOC功能,在解耦上达到了配置级别. 第二是使用它对数据库访问事务相关的封装. 第三就是各种其他组件与Spring的融合,在Sp ...

  3. 深挖Jvm垃圾收集

    垃圾收集(Garbage Collection,GC),它的任务是解决以下 3 件问题: 哪些内存需要回收? 什么时候回收? 如何回收? 其中第一个问题很好回答,在 Java 中,GC 主要发生在 J ...

  4. mask-rcnn解读(二):clip_boxes_graph()

    此函数是利用deltas对box修正,我并没有详细说明,若有问题,欢迎留言交流: def clip_boxes_graph(boxes, window): """ box ...

  5. tf.image.adjust_brightness等的使用

    import tensorflow as tfimport numpy as npimport cv2 as cvimport matplotlib.pyplot as pltsess=tf.Sess ...

  6. Unity Ioc 依赖倒置及Untity AOP被动拦截/自动拦截

    各位博友金安,首先声明这是一篇转载的博客,原文链接:https://www.cnblogs.com/scottpei/archive/2013/01/08/2851087.html 十年河东,十年河西 ...

  7. EntityFramework 基类重写

    /* * ------------------------------------------------------------------------------ * * 创 建 者:F_Gang ...

  8. 如何构思你的iOS App应用

    转自:http://mobile.51cto.com/hot-311134.htm 在twitter上,听到不少人问,我也有App 在App Store上面卖,为什么我的App卖得这么少啊?于是我就上 ...

  9. Spring常用注解之一

    Spring中的常用注解 @Component 把普通 pojo 实例化到 Spring 容器中,相当于配置文件中的 泛指各种组件,就是说当我们的类不属于各种归类的时候(不属于@Controller. ...

  10. while(n)什么意思?

    1.n是bool型变量时,就是代表n为true时运行循环(bool表示布尔型变量,也就是逻辑型变量的定义符,以英国数学家.布尔代数的奠基人乔治·布尔(George Boole)命名. bool类似于f ...