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. vs2017安装pygame,vs2017安装python第三方包

    vs2017有独立的python环境:所以想在vs2017开发python并使用第三方包,需要在vs2017中操作,完成第三方包的安装. 一,查看vs2017有哪些版本的python,当前使用的是哪个 ...

  2. "PECS原则"几篇好文章

    <? extends T>和<? super T>Java 泛型中的PECS原则(copy源码样例) ​

  3. 接口测试之深入理解HTTPS

    前言 随着网络安全问题越来越被重视,HTTPS协议的使用已经逐渐主流化.目前的主流站点均已使用了HTTPS协议:比如:百度.淘宝.京东等一二线主站都已经迁移到HTTPS服务之上.而作为测试人员来讲,也 ...

  4. JavaScript是如何工作的:深入类和继承内部原理 + Babel和TypeScript 之间转换

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 15 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  5. Java中的会话Cookie&&Session

    会话技术 会话: 一次会话中包含多次请求和响应. 一次会话:浏览器第一次给服务器资源发送请,会话建立,直到有一方断开为止 功能:在一次会话的范围内的多次请求之间共享数据 方式: 客户端会话技术:coo ...

  6. 商汤科技汤晓鸥:其实不存在AI行业,唯一存在的是“AI+“行业

    https://mp.weixin.qq.com/s/bU-TFh8lBAF5L0JrWEGgUQ 9 月 17 日,2018 世界人工智能大会在上海召开,在上午主论坛大会上,商汤科技联合创始人汤晓鸥 ...

  7. pip 使用豆瓣源

    pip 使用豆瓣源 由于pip 默认使用Python的官方源pypi.python.org/pypi,导致我们经常使用pip装包时速度过慢或者无法安装(请求超时)等问题, 所以国内用户建议使用pip ...

  8. 关于flutter插件地图的使用flutter_map

    关于flutter插件地图的使用flutter_map flutter_map A Dart implementation of Leaflet for Flutter apps.一个基于leafle ...

  9. MUI ios下用video标签默认全屏播放

    前几天用Hbuilder+MUI做了个应用,里边用到<video>标签,在Android下正常,但是在苹果手机老是默认全屏播放. 解决办法: 首先在video标签加上playsinline ...

  10. python爬虫实战:利用scrapy,短短50行代码下载整站短视频

    近日,有朋友向我求助一件小事儿,他在一个短视频app上看到一个好玩儿的段子,想下载下来,可死活找不到下载的方法.这忙我得帮,少不得就抓包分析了一下这个app,找到了视频的下载链接,帮他解决了这个小问题 ...