可重入锁ReentrantLock
ReentrantLock 重入锁,是实现Lock 接口 的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源重复加锁,即当前线程获取该锁后再次获取不会被阻塞。
要想支持重入性,就要解决两个问题:
- 在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功;
- 由于锁会被获取 n 次,那么只有锁在被释放同样的 n 次之后,该锁才算是完全释放成功。
ReentrantLock 源码分析
// 内部类 Sync 的 nonfairTryAcquire 方法
// 每次重新获取都会对同步状态进行加一的操作
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 1. 如果该锁未被任何线程占有,该锁能被当前线程获取
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 2.若被占有,检查占有线程是否是当前线程
else if (current == getExclusiveOwnerThread()) {
// 3. 再次获取,计数加一
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
// 内部类 Sync 的 tryRelease 方法
// 重入锁的释放必须得等到同步状态为 0 时锁才算成功释放
protected final boolean tryRelease(int releases) {
//1. 同步状态减1
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//2. 只有当同步状态为0时,锁成功被释放,返回true
free = true;
setExclusiveOwnerThread(null);
}
// 3. 锁未被完全释放,返回false
setState(c);
return free;
}
ReentrantLock 支持两种锁:公平锁和非公平锁。何谓公平性,是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求上的绝对时间顺序,满足 FIFO。
// ReentrantLock 的构造方法无参时是构造非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 可传入一个 boolean 值,true 时为公平锁,false 时为非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
在非公平锁获取时(nonfairTryAcquire 方法),只是简单的获取了一下当前状态然后做了一些逻辑处理,并没有考虑到当前同步队列中线程等待的情况。
公平锁的处理逻辑:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 增加了 hasQueuedPredecessors
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 的逻辑判断,从方法名就可以知道该方法用来判断当前节点在同步队列中是否有前驱节点的,如果有前驱节点,说明有线程比当前线程更早的请求资源,根据公平性,当前线程请求资源失败。如果当前节点没有前驱节点,才有做后面逻辑判断的必要性。
公平锁每次都是让同步队列中的第一个节点获取到锁,而非公平性锁则不一定,有可能刚释放锁的线程能再次获取到锁。
ReentrantLock 使用
public class ReentrantLockTest {
private static final ReentrantLock lock = new ReentrantLock();
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
// 当前线程获取该锁后再次获取不会被阻塞
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
// 锁必须在 try 代码块开始之前获取,并且加锁之前不能有异常抛出,否则在 finally 块中就无法释放锁(ReentrantLock 的锁必须在 finally 中手动释放)。
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(count);
}
}
ReentrantLock 与 synchronized
ReentrantLock 是一个类,而 synchronized 是 Java 中的关键字;
ReentrantLock 可以实现多路选择通知(可以绑定多个 Condition)),而 synchronized 只能通过 wait 和 notify/notifyAll 方法唤醒一个线程或者唤醒全部线程(单路通知);
ReentrantLock 必须手动释放锁。通常需要在 finally 块中调用 unlock 方法以确保锁被正确释放。
synchronized 会自动释放锁,当同步块执行完毕时,由 JVM 自动释放,不需要手动操作。
ReentrantLock: 通常提供更好的性能,特别是在高竞争环境下。
synchronized: 在某些情况下,性能可能稍差一些,但随着 JDK 版本的升级,性能差距已经不大了。
可重入锁ReentrantLock的更多相关文章
- synchronized关键字,Lock接口以及可重入锁ReentrantLock
多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...
- Java 重入锁 ReentrantLock 原理分析
1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...
- 轻松学习java可重入锁(ReentrantLock)的实现原理
转载自https://blog.csdn.net/yanyan19880509/article/details/52345422,(做了一些补充) 前言 相信学过java的人都知道 synchroni ...
- java 可重入锁ReentrantLock的介绍
一个小例子帮助理解(我们常用的synchronized也是可重入锁) 话说从前有一个村子,在这个村子中有一口水井,家家户户都需要到这口井里打水喝.由于井水有限,大家只能依次打水.为了实现家家有水喝,户 ...
- 轻松学习java可重入锁(ReentrantLock)的实现原理(转 图解)
前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...
- 17_重入锁ReentrantLock
[概述] 重入锁可以完全代替synchronized关键字. 与synchronized相比,重入锁ReentrantLock有着显示的操作过程,即开发人员必须手动指定何时加锁,何时释放锁,所以重入锁 ...
- Java 显示锁 之 重入锁 ReentrantLock(七)
ReentrantLock 重入锁简介 重入锁 ReentrantLock,顾名思义,就是支持同一个线程对资源的重复加锁.另外,该锁还支持获取锁时的公平与非公平性的选择. 重入锁 ReentrantL ...
- Java中可重入锁ReentrantLock原理剖析
本文由码农网 – 吴极心原创,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划! 一. 概述 本文首先介绍Lock接口.ReentrantLock的类层次结构以及锁功能模板类AbstractQue ...
- Java多线程——深入重入锁ReentrantLock
简述 ReentrantLock 是一个可重入的互斥(/独占)锁,又称为“独占锁”. ReentrantLock通过自定义队列同步器(AQS-AbstractQueuedSychronized,是实现 ...
- java线程的同步控制--重入锁ReentrantLock
我们常用的synchronized关键字是一种最简单的线程同步控制方法,它决定了一个线程是否可以访问临界区资源.同时Object.wait() 和Object.notify()方法起到了线程等待和通知 ...
随机推荐
- MindSpore社区的小礼物
前段时间在gitee上给MindSpore提过几个问题,后来一直没有回复,前几天得到了回复又通过网络视频聊了聊自己认为该开源项目还有哪些不足的地方,最后获赠了个小书包,对这个小礼物还是蛮喜欢的,下面给 ...
- iOS开发基础149-由UUIDString引发的思考
问题1:[[UIDevice currentDevice] identifierForVendor].UUIDString什么情况下值会变化? [[UIDevice currentDevice] id ...
- 这是DDD建模最难的部分(其实很简单)
本文书接上回<为了落地DDD,我是这样"PUA"大家的> ,欢迎关注我的同名公众号. https://mp.weixin.qq.com/s/DjC0FSWY1bgJ ...
- Codeforces Round894.D
题目:D. Ice Cream Balls 题目链接:https://codeforces.com/contest/1862/problem/D 思路:二分找到当所有冰淇淋球类型不同的情况下,假设记位 ...
- Tree组件的快速定位更新节点的状态,以及修改节点的数据属性等操作
当我们点击树节点的时候我们常常只能获得树的id,那么我么如何获快速定位到树节点的内容呢,除此之外,当树已经存在时,但是缺少我们想要的内容时,我们想在树节点上添加我们需要的额外的内容时该怎么办,那么就是 ...
- linux小常识
一.一般来说著名的linux系统基本上分两大类:1 RedHat系列:Redhat.Centos.Fedora等2 Debian系列:Debian.Ubuntu等RedHat 系列:1 常见的安装包格 ...
- 安装部署harbor
配置文件中修改内容hostname 和 证书 私钥 需要指定到文件 hostname: hub.rainsc.com # http related config http: # port for ht ...
- Docker容器常用操作命令(镜像的上传、下载、导入、导出、创建、删除、修改、启动等)详解
1.docker镜像下载 docker pull [options] name [:tag@digest] name后边可以跟镜像标签或者镜像摘要(其实就是镜像的版本),如果不加任何东西,则会默认是在 ...
- 网络安全公开数据集Maple-IDS,恶意流量检测数据集开放使用!
Maple Dataset 枫叶数据集Maple Dataset枫叶数据集由东北林业大学网络安全实验室(https://maple.nefu.edu.cn/lab/)公开发布,是用于入侵检测评估的数据 ...
- vue 根据div id 滚动到指定view到可视视图中
<div id="name" @click="scrollToViewById('name')"> ...... </div> scro ...