ReentrantLock是什么?

  1. ReentrantLock重入锁,递归无阻塞的同步机制,实现了Lock接口;
  1. 能够对共享资源重复加锁,即当前线程获取该锁,再次获取不会被阻塞;
  1. 支持公平锁和非公平锁。

UML图

  1. 公平锁
 
   2.非公平锁
 

问题:

    1. 重入性的实现原理;
    1. 公平和非公平锁。

ReentrantLock的使用

//非公平锁
ReentrantLock lock = new ReentrantLock();
lock.lock();
//lock方法:
public void lock() {
sync.lock();
}
注释:
  1. Sync为ReentrantLock里面的一个内部类,它继承AQS(AbstractQueuedSynchronizer),它有两个子类:公平锁FairSync和非公平锁NonfairSync。
  1. ReentrantLock里面大部分的功能都是委托给Sync来实现的,同时Sync内部定义了lock()抽象方法由其子类去实现,默认实现了nonfairTryAcquire(int acquires)方法,可以看出它是非公平锁的默认实现方式。下面我们看非公平锁的lock()方法:
final void lock() {
//尝试获取锁
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//获取失败,调用AQS的acquire(int arg)方法
acquire(1);
}
  1. 首先会第一次尝试快速获取锁,如果获取失败,则调用acquire(int arg)方法,该方法定义在AQS中,如下:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
  1. 这个方法首先调用tryAcquire(int arg)方法,在AQS中讲述过,tryAcquire(int arg)需要自定义同步组件提供实现,非公平锁实现如下:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
} final boolean nonfairTryAcquire(int acquires) {
//当前线程
final Thread current = Thread.currentThread();
//获取同步状态
int c = getState();
//state == 0,表示没有该锁处于空闲状态
if (c == 0) {
//获取锁成功,设置为当前线程所有
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return 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;
}

释放锁

  1. 获取同步锁后,使用完毕则需要释放锁,ReentrantLock提供了unlock释放锁:
 public void unlock() {
sync.release(1);
}
  1. unlock内部使用Sync的release(int arg)释放锁,release(int arg)是在AQS中定义的:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
  1. 与获取同步状态的acquire(int arg)方法相似,释放同步状态的tryRelease(int arg)同样是需要自定义同步组件自己实现:
protected final boolean tryRelease(int releases) {
//减掉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;
}

只有当同步状态彻底释放后该方法才会返回true。当state == 0 时,则将锁持有线程设置为null,free= true,表示释放成功。

重入性的实现原理
  1. 在线程获取锁时,如果已经获取锁的线程是当前线程,则直接再次获取成功;
  1. 锁会被获取n次,只有锁在被释放同样n次之后,该锁才会被完全释放。
重入锁--非公平锁--加锁的实现原理
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair tryfor trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果该锁未被任何线程占有,该锁能被当前线程获取
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return 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;
}

注释:如果该锁未被任何线程占有,该锁能被当前线程获取,如果该锁被占有,检查占有线程是否是当前线程,如果是的话,同步状态加一并返回true。

 

重入锁--非公平锁--解锁的实现原理

源代码java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire
protected final boolean tryRelease(int releases) {
//当前线程的同步状态减一
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//只用同步状态为0的时候,锁才能释放成功,返回true
free = true;
setExclusiveOwnerThread(null);
}
//当锁未完全释放,返回false
setState(c);
return free;
}

注释:

当前线程的同步状态减一, 只用同步状态为0的时候,锁才能释放成功,返回true, 当锁未完全释放,返回false。
公平性锁
锁的获取顺序符合请求上的绝对时间顺序,满足FIFO,
  1. ReentrantLock的构造方法,无参时是构造非公平锁,源码为:
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
  1. ReentrantLock的构造方法,带boolean参数,可传入一个boolean值,true时为公平锁,false时为非公平锁,源码为:
/**
* Creates an instance of {@codeReentrantLock} with the
* given fairness policy.
*
* @paramfair {@codetrue} ifthis lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

重入锁--公平锁--加锁的实现原理

源代码:java.util.concurrent.locks.ReentrantLock.FairSync#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)) {
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;
}

注释:

public final boolean hasQueuedPredecessors() {
Node t = tail; //尾节点
Node h = head; //头节点
Node s; //头节点 != 尾节点
//同步队列第一个节点不为null
//当前线程是同步队列第一个节点
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
//该方法主要做一件事情:主要是判断当前线程是否位于CLH同步队列中的第一个。如果是则返回true,否则返回false。
  1. 代码和非公平锁基本一直,唯一的不同在于增加了hasQueuedPredecessors的逻辑判断。该方法用来判断当前节点在同步队列中是否有前驱节点。如果有前驱节点说明有线程比当前线程更早的请求资源,根据公平性,当前线程请求资源失败。如果当前节点没有前驱节点,猜能够做后面的逻辑判断,
  1. 公平锁每次都是从同步队列中的对一个节点获取到锁,而非公平性锁则不一定,有可能刚释放锁的线程能够再次获取到锁。
公平锁和非公平锁的比较
  1. 公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序,而非公平锁有可能刚释放锁的线程下次继续获取该锁,则有可能导致其他线程永远无法获取到锁,造成“饥饿”现象。
  1. 公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销。因此,ReentrantLock默认选择的是非公平锁,则是为了减少一部分上下文切换,保证了系统更大的吞吐量。
 
 
参考链接:
  1. https://www.javazhiyin.com/15047.html
  1. https://www.javazhiyin.com/6265.html

Java并发--ReentrantLock原理详解的更多相关文章

  1. java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock

    原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...

  2. Java并发关键字Volatile 详解

    Java并发关键字Volatile 详解 问题引出: 1.Volatile是什么? 2.Volatile有哪些特性? 3.Volatile每个特性的底层实现原理是什么? 相关内容补充: 缓存一致性协议 ...

  3. java的ReentrantLock类详解

    ReentrantLock 能用于更精细化的加锁的Java类, 通过它能更清楚了解Java的锁机制 ReentrantLock 类的集成关系有点复杂, 既有内部类, 还有多重继承关系 类的定义 pub ...

  4. JAVA线程池原理详解二

    Executor框架的两级调度模型 在HotSpot VM的模型中,JAVA线程被一对一映射为本地操作系统线程.JAVA线程启动时会创建一个本地操作系统线程,当JAVA线程终止时,对应的操作系统线程也 ...

  5. Java虚拟机工作原理详解 (一)

    一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了解java程序工作原理.首先,你写好java代码,保存到硬盘当中.然后你在命令行中输入 javac YourClassNam ...

  6. Java虚拟机工作原理详解

    原文地址:http://blog.csdn.net/bingduanlbd/article/details/8363734 一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了 ...

  7. java并发编程 | 线程详解

    个人网站:https://chenmingyu.top/concurrent-thread/ 进程与线程 进程:操作系统在运行一个程序的时候就会为其创建一个进程(比如一个java程序),进程是资源分配 ...

  8. [转]java虚拟机工作原理详解

    一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了解java程序工作原理.首先,你写好java代码,保存到硬盘当中.然后你在命令行中输入 javac YourClassNam ...

  9. JAVA线程池原理详解一

    线程池的优点 1.线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用. 2.可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃. 线 ...

随机推荐

  1. pycharm安装Numba失败问题

    相关环境变量: pycharm Python 3.8 pip 19.3.1 Numba各个版本都不行 报错内容: Looking in indexes: https://pypi.tuna.tsing ...

  2. PHP基础-自定义函数-变量范围-函数参数传递

    一.自定义函数    function 函数名([形式参数1,形式参数2,....形式参数n]){        //各种PHP代码....        //......        return ...

  3. uniapp轻轻松松开发各种类型的小程序

    1.前言 现在移动端用户使用量占据了市场大部分的比例,今天 给大家说说怎么去开发一个小程序,今天使用的是uniapp 2.什么是uniapp uni-app 是一个使用 Vue.js 开发所有前端应用 ...

  4. SD.Team主题形象小人偶

              W e ♥ S D     ♫ ♪ 咔咔咔~可能源码冲突会造成小人偶光头 :)

  5. Java IO(八) PipedInputStream 和 PipedOutputStream

    Java IO(八) PipedInputStream 和 PipedOutputStream 一.介绍 PipedInputStream 和 PipedOutputStream 是管道输入流和管道输 ...

  6. MySQL 可重复读,差点就我背上了一个 P0 事故!

    小黑黑的碎碎念 哎,最近有点忙,备考复习不利,明天还要搬家,好难啊!! 本想着这周鸽了,但是想想还是不行,爬起来,更新一下,周更可不能断.偷懒一下,修改一下之前的一篇历史文章,重新发布一下. P0 事 ...

  7. 重学 Java 设计模式:实战单例模式

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 5个创建型模式的最后一个 在设计模式中按照不同的处理方式共包含三大类:创建型模式.结 ...

  8. java方法句柄-----3.方法句柄的实现接口

    目录 1.使用方法句柄实现接口 1.使用方法句柄实现接口   2.3节介绍的动态代理机制可以在运行时为多个接口动态创建实现类,并拦截通过接口进行的方法调用.方法句柄也具备动态实现一个接口的能力.这是通 ...

  9. Java实现 LeetCode 385 迷你语法分析器

    385. 迷你语法分析器 给定一个用字符串表示的整数的嵌套列表,实现一个解析它的语法分析器. 列表中的每个元素只可能是整数或整数嵌套列表 提示:你可以假定这些字符串都是格式良好的: 字符串非空 字符串 ...

  10. Java实现 LeetCode 57 插入区间

    57. 插入区间 给出一个无重叠的 ,按照区间起始端点排序的区间列表. 在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间). 示例 1: 输入: inte ...