1.定义

重入锁:能够支持一个线程对资源的重复加锁,也就是当一个线程获取到锁后,再次获取该锁时而不会被阻塞。

2.可重入锁的应用场景

2.1 如果已经加锁,则不再重复加锁,比如:交互界面点击后响应时间长,可能会多次点击,使用重入锁可防止后台重复执行。

if (lock.tryLock()) {  //如果已经被lock,则立即返回false不会等待,达到忽略操作的效果
try {
//操作
} finally {
lock.unlock();
}
}

2.2 如果发现该操作已经在执行,则等待一段时间,超时则不再执行。

try {
if (lock.tryLock(5, TimeUnit.SECONDS)) { //如果已经被lock,尝试等待5s,看是否可以获得锁,如果5s后仍然无法获得锁则返回false继续执行
try {
//操作
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace(); //当前线程被中断时(interrupt),会抛InterruptedException
}

2.3 如果操作已经加锁,则等待一个一个加锁。可以防止资源使用冲突,保证同一个时间内只有一个操作可以使用该资源。

lock.lock(); //如果被其它资源锁定,会在此等待锁释放,也将处于阻塞状态

2.4 可中断锁:可响应中断操作,然后释放锁。

lock.lockInterruptibly();

3.原理分析

可重入锁在获取锁时,有公平和非公平获取锁两种方式,默认是后者。如果在绝对时间上,先对锁进行获取的请求先被满足,那么这个锁则是公平锁,也就是等待时间最长的线程优先获取锁。反之,则是非公平锁,非公平锁的效率较高。我们知道ReentrantLock

是通过使用自定义同步器来实现锁的获取和释放的,那么就看一下获取同步状态的代码。

3.1 非公平锁获取状态的代码

 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;
}

以上代码逻辑是这样的:当一个线程尝试获取锁时,先判断同步状态值是否为0,如果是,则该现场获取到锁,并返回true;如果同步状态值不为0,则判断该线程是否就是占用该锁的当前线程,如果是,则同步状态值增加,表示同一个线程多次加锁,并返回ture。否则,则获取同步状态值失败,返回false。

3.2 释放状态时的代码

        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);
}
setState(c);
return free;
}

在释放锁时,每释放一次,同步状态值就是减少,最终同步状态值为0时,表示完全释放锁,此时将占有线程设置为null,同时返回true。

3.3 公平锁获取状态时的代码

        /**
* 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;
}
}

这和nonfairTryAcquire方法唯一的区别就是增加了hasQueuedPredecessors()方法,该方法的作用就是判断同步队列中当前节点是否有前驱节点。如果该方法返回true,则表示有线程比当前线程更早地请求获取锁,因此需要等待前驱线程获取并释放锁之后才能

继续获取锁。

现在有个问题需要思考,为什么非公平锁效率更高?

在nonfairTryAcquire(int acquires)方法中,当一个线程请求锁时,只要获取了同步状态即成功获取锁。在这个前提下,刚释放锁的线程再次获取同步状态的几率会非常大,使得其他线程只能在同步队列中等待。它虽然造成了线程饥饿,但是确实极少的线程切换,

保证了更达的吞吐量。而公平性锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。

ReentrantLock重入锁详解的更多相关文章

  1. “全栈2019”Java多线程第二十九章:可重入锁与不可重入锁详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. 从源码角度彻底理解ReentrantLock(重入锁)

    目录 1.前言 2.AbstractQueuedSynchronizer介绍 2.1 AQS是构建同步组件的基础 2.2 AQS的内部结构(ReentrantLock的语境下) 3 非公平模式加锁流程 ...

  3. java并发系列(四)-----源码角度彻底理解ReentrantLock(重入锁)

    1.前言 ReentrantLock可以有公平锁和非公平锁的不同实现,只要在构造它的时候传入不同的布尔值,继续跟进下源码我们就能发现,关键在于实例化内部变量sync的方式不同,如下所示: /** * ...

  4. ReentrantLock(重入锁)以及公平性

    ReentrantLock(重入锁)以及公平性 标签(空格分隔): java NIO 如果在绝对时间上,先对锁进行获取的请求一定被先满足,那么这个锁是公平的,反之,是不公平的,也就是说等待时间最长的线 ...

  5. java高并发系列 - 第12天JUC:ReentrantLock重入锁

    java高并发系列 - 第12天JUC:ReentrantLock重入锁 本篇文章开始将juc中常用的一些类,估计会有十来篇. synchronized的局限性 synchronized是java内置 ...

  6. ReentrantLock(重入锁)的源码解析

    转自:从源码角度彻底理解ReentrantLock(重入锁)](https://www.cnblogs.com/takumicx/p/9402021.html)) 公平锁内部是FairSync,非公平 ...

  7. Java多线程之ReentrantLock重入锁简介与使用教程

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6543947.html  我们知道,线程安全问题需要通过线程之间的同步来解决,而同步大多使用syncrhoize ...

  8. ReentrantLock 重入锁(下)

    前沿:       ReentrantLock 是java重入锁一种实现,在java中我们通常使用ReentrantLock 和 synchronized来实现锁功能,本篇通过例子来理解下Reentr ...

  9. ReentrantLock(重入锁)简单源码分析

    1.ReentrantLock是基于AQS实现的一种重入锁. 2.先介绍下公平锁/非公平锁 公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁. 非公平锁 非公平锁是指多个线程获取锁的顺序并不是按照申 ...

随机推荐

  1. [android] 相对布局和单位简介

    /**********************2016年4月23日 更新*******************************/ 知乎:为何Android的默认布局是RelativeLayou ...

  2. chrome设置网页编码

    新版的 chrome 没有这个设置选项,可以借助插件 charset 插件实现,到 web store 搜索安装即可.

  3. 委托(2).net 1.x中的委托

    上一篇已经演示了使用委托实现一个多语言问候的程序,这一篇文章来总结一下在.net 1.x中委托的使用方法. 既然委托是一个类型(class),那么它就要经历像类一个先声明,然后new一个对象,最后调用 ...

  4. hbuilder vs2013api Access-Control-Allow-Origin

    <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" valu ...

  5. Kruskal重构树入门

    这个知识点好像咕咕咕了好长了..趁还没退役赶紧补一下吧.. 讲的非常简略,十分抱歉.. 前置知识 Kruskal算法 一定的数据结构基础(如主席树) Kruskal重构树 直接bb好像不是很好讲,那就 ...

  6. ArcGIS API for JavaScript 4.2学习笔记[20] 使用缓冲区结合Query对象进行地震点查询【重温异步操作思想】

    这个例子相当复杂.我先简单说说这个例子是干啥的. 在UI上,提供了一个下拉框.两个滑动杆,以确定三个参数,使用这三个参数进行空间查询.这个例子就颇带空间查询的意思了. 第一个参数是油井类型,第二个参数 ...

  7. 小程序实践(十一):showModal的使用

    显示模态对话框,确定和取消两个选择+标题+内容的对话框 . // 编辑用户性别 tapSex:function(res){ var that = this wx.showModal({ title: ...

  8. 实现MongoDB读写分离的“读偏好”介绍

    在某些情况下,将读请求发送给副本集的备份节点是合理的,例如,单个服务器无法处理应用的读压力,就可以把查询请求路由到可复制集中的多台服务器上.现在绝大部分MongoDB驱动支持读偏好设置(read pr ...

  9. T-SQL_select语句详解

    select语句执行的过程: 先看查询内容 ==>where条件 ==>[分组条件] ==>[分组搜索条件] ==>内容输出 ==>[是否排序] SQL中SELECT语句 ...

  10. 从0开始的Python学习008变量

    局部变量 在我们定义函数的过程中,函数内外具有相同名称的变量是没有任何关系的.变量的名称对于函数来说是局部的,而它所在的代码块就是它的作用域. 使用局部变量 #局部变量 def func(x): pr ...