多线程锁
在并发编程中,锁(Lock)是一种用于控制多个线程对共享资源访问的机制。正确使用锁可以确保数据的一致性和完整性,避免出现竞态条件(Race Condition)、死锁(Deadlock)等问题。Java 提供了多种类型的锁来满足不同场景下的需求,从内置的对象锁到更高级别的 java.util.concurrent.locks
包中的显式锁。
为什么需要锁?
当多个线程同时尝试修改同一份数据时,如果没有适当的同步措施,可能会导致以下问题:
- 数据不一致:不同的线程读取到了部分更新的数据。
- 丢失更新:一个线程的更改被另一个线程覆盖。
- 脏读/写:线程读取或写入了尚未完成的操作。
为了解决这些问题,我们需要一种机制来协调线程之间的访问顺序,保证每次只有一个线程能够操作共享资源。这就是锁的作用所在。
Java 中的锁类型
内置锁(Intrinsic Lock)
也称为监视器锁(Monitor Lock),是通过关键字 synchronized
实现的。每个对象都有一个与之关联的内置锁,当一个线程进入由 synchronized
修饰的方法或代码块时,它会自动获取该对象的锁;而当方法执行完毕或遇到 wait()
调用时,则会释放锁。
public synchronized void method() {// 只有一个线程可以进入这里
}
public void method() {synchronized (this) {// 同样只有一个线程可以进入这里}
}
显式锁(Explicit Lock)
从 Java 5 开始,java.util.concurrent.locks
包引入了更为灵活和强大的锁接口——Lock
。相比于内置锁,显式锁提供了更多的功能,如可中断的锁等待、超时获取锁以及公平锁等特性。
ReentrantLock
ReentrantLock
是最常用的显式锁实现之一,它允许同一个线程多次获取同一把锁,并且必须按照获取的次数逐一释放。这使得递归调用成为可能。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Counter {private int count = 0;private final Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock(); // 确保无论如何都会解锁}}public int getCount() {return count;}
}
ReadWriteLock
ReadWriteLock
接口定义了一种读写分离的锁机制,其中读锁允许多个线程同时持有,但不允许写锁存在;反之,一旦有线程持有了写锁,其他所有试图获取读锁或写锁的线程都将被阻塞。这种设计非常适合读多写少的情况。
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class Cache {private final Map<String, Object> map = new HashMap<>();private final ReadWriteLock rwl = new ReentrantReadWriteLock();public Object get(String key) {rwl.readLock().lock();try {return map.get(key);} finally {rwl.readLock().unlock();}}public void put(String key, Object value) {rwl.writeLock().lock();try {map.put(key, value);} finally {rwl.writeLock().unlock();}}
}
StampedLock
StampedLock
是 Java 8 新增的一种乐观锁,它结合了读写锁的功能并提供了更好的性能优化。它支持三种模式:读、写和乐观读。乐观读模式假设不会有冲突发生,因此不会阻塞其他线程;但如果检测到冲突,则需要回滚操作并重新尝试。
import java.util.concurrent.locks.StampedLock;public class Point {private double x, y;private final StampedLock sl = new StampedLock();void move(double deltaX, double deltaY) { long stamp = sl.writeLock();try {x += deltaX;y += deltaY;} finally {sl.unlockWrite(stamp);}}double distanceFromOrigin() { long stamp = sl.tryOptimisticRead();double currentX = x, currentY = y;if (!sl.validate(stamp)) {stamp = sl.readLock();try {currentX = x;currentY = y;} finally {sl.unlockRead(stamp);}}return Math.sqrt(currentX * currentX + currentY * currentY);}
}
锁的使用原则
- 最小化锁定范围:尽量减少加锁的时间和区域,只保护真正需要保护的资源。
- 避免死锁:设计时要特别小心,防止形成循环等待链,即多个线程互相持有对方所需的锁。
- 使用超时机制:对于可能长时间阻塞的操作,考虑设置合理的超时时间以提高系统的健壮性。
- 优先选择高级并发工具:相比于原始的内置锁,应该更多地利用
java.util.concurrent
包中提供的高级工具,因为它们通常更加安全可靠且易于使用。 - 文档化锁协议:清晰地记录各个锁之间的关系和使用规则,有助于后续维护人员理解代码逻辑。
最佳实践案例
使用 ReentrantLock
替代内置锁
在某些情况下,ReentrantLock
可以为我们提供比内置锁更多的灵活性。例如,它可以让我们实现非阻塞的锁尝试 (tryLock
) 或者带超时的锁等待 (tryLock(long timeout, TimeUnit unit)
).
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;public class BankAccount {private double balance;private final Lock lock = new ReentrantLock();public boolean withdraw(double amount) throws InterruptedException {if (lock.tryLock(1, TimeUnit.SECONDS)) {try {if (balance >= amount) {balance -= amount;return true;}} finally {lock.unlock();}}return false; // 超时未获得锁或余额不足}
}
使用 ReadWriteLock
优化读多写少场景
当应用程序中有大量读操作而写操作相对较少时,ReadWriteLock
可以显著提升性能,因为它允许多个线程同时进行读取,而不必像内置锁那样每次都排他地占用资源。
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ConcurrentCache<K, V> {private final Map<K, V> cache = new ConcurrentHashMap<>();private final ReadWriteLock rwl = new ReentrantReadWriteLock();public V get(K key) {rwl.readLock().lock();try {return cache.get(key);} finally {rwl.readLock().unlock();}}public void put(K key, V value) {rwl.writeLock().lock();try {cache.put(key, value);} finally {rwl.writeLock().unlock();}}
}
使用 StampedLock
进行乐观读
对于那些读操作远多于写操作,并且读操作之间几乎没有竞争的应用程序来说,StampedLock
的乐观读模式可以提供更高的吞吐量。它首先尝试无锁读取,只有在检测到潜在冲突时才会退回到传统的读锁方式。
import java.util.concurrent.locks.StampedLock;public class OptimisticCounter {private long count = 0;private final StampedLock sl = new StampedLock();public long readCount() {long stamp = sl.tryOptimisticRead();long localCount = count;if (!sl.validate(stamp)) {stamp = sl.readLock();try {localCount = count;} finally {sl.unlockRead(stamp);}}return localCount;}public void increment() {long stamp = sl.writeLock();try {count++;} finally {sl.unlockWrite(stamp);}}
}
结语
感谢您的阅读!如果您对多线程锁或其他并发编程话题有任何疑问或见解,欢迎继续探讨。
相关文章:

多线程锁
在并发编程中,锁(Lock)是一种用于控制多个线程对共享资源访问的机制。正确使用锁可以确保数据的一致性和完整性,避免出现竞态条件(Race Condition)、死锁(Deadlock)等问题。Java 提供…...

ZooKeeper 核心知识全解析:架构、角色、节点与应用
1.ZooKeeper 分布式锁怎么实现的 ZooKeeper 是一个高效的分布式协调服务,它提供了简单的原语集来构建更复杂的同步原语和协调数据结构。利用 ZooKeeper 实现分布式锁主要依赖于它的顺序节点(Sequential Node)特性以及临时节点(Ep…...

笔记本电脑 选购 回收 特权模式使用 指南
笔记本电脑 factor 无线网卡:有些笔记本无法检测到特定频段的信息,会导致连不上校园网 sudo iwlist wlp2s0 scan | grep Frequency > net.txt cat net.txt>表示用终端输出覆盖后续文件,>>表示添加到后续文件的末尾 一种更简…...

2023-2024 学年 广东省职业院校技能大赛(高职组)“信息安全管理与评估”赛题一
2023-2024 学年 广东省职业院校技能大赛(高职组“信息安全管理与评估”赛题一) 模块一:网络平台搭建与设备安全防护第一阶段任务书任务 1:网络平台搭建任务 2:网络安全设备配置与防护DCRS:DCFW:DCWS:DCBC:WAF: 模块二:网络安全事件…...

C#补充----反射,特性,迭代器,特殊语法,值类型运用类型。
1.反射:通过type 获取类中的数据。创建实例,并赋值。 《1》获取类的方式 《2》反射的应用 <1>获取类型的所有公共成员 <2>获取构造函数 <3>获取类型的 公共成员变量 <4>获取类型的 公共方法 <5>.获取类型的 属性 <6&g…...

深度学习核函数
一、核函数的基本概念 核函数在机器学习中具有重要应用价值,常用于支持向量机(SVM)等算法中。 核函数是面试中经常被考到的知识点,对于找工作和实际数据转换都有重要作用。 二、数据建模与核函数的作用 数据越多,可…...

Spring MVC流程一张图理解
由于现在项目中大部分都是使用springboot了,但是ssm中的springmvc还是可以了解一下 1 、用户发送请求至前端控制器 DispatcherServlet 。 2 、 DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。 3 、处理器映射器找到具体的处理器 ( 可以根据 xml 配…...

计算机网络速成
前言:最近在做一些动态的crypto,但是配置总搞不好,正好也有学web的想法,就先学学web再回去做密码,速成视频推荐b站建模老哥 目录 计算机网络概述网络的范围分级电路交换网络(电路交换)报文交换网…...

spring.profiles.active不同优先级
1、在editConfiguration中配置profiles.activedev会同时影响项目取application-dev.properties、bootstrap-dev.yaml,且这种方式优先级最高,会覆盖application.properties、bootstrap.yaml中的spring.profiles.active配置 2、在application.properties配…...

我这不需要保留本地修改, 只需要拉取远程更改
如果你不需要保留本地修改,只需要拉取远程更改并强制将本地分支与远程分支同步,可以按照以下步骤操作: 1. 丢弃本地修改 首先,丢弃所有本地未提交的修改: git reset --hard这会重置工作目录和暂存区,丢弃…...

源码编译安装httpd 2.4,提供系统服务管理脚本并测试(两种方法实现)
下载 httpd 2.4 源码: wget https://dlcdn.apache.org/httpd/httpd-2.4.x.tar.gztar -zxvf httpd-2.4.x.tar.gzcd httpd-2.4.x配置、编译和安装: ./configure --prefix/usr/local/apache2 --enable-so --enable-ssl --enable-cgi makesudo make install实…...

深度学习在自动化测试中的创新应用:提升运维效率与质量
《深度学习在自动化测试中的创新应用:提升运维效率与质量》 一、引言 在当今快速发展的软件行业中,自动化测试是确保软件质量和可靠性的关键环节。随着软件系统的日益复杂,传统的自动化测试方法在处理复杂场景、提高测试覆盖率和准确性方面面临着诸多挑战。深度学习作为人…...

单独编译QT子模块
单独编译QT子模块 系统 win qt-everywhere-src-5.12.12 下载源码: https://download.qt.io/archive/qt/5.12/5.12.12/single/ 参考: https://doc.qt.io/qt-5/windows-building.html 安装依赖 https://doc.qt.io/qt-5/windows-requirements.html Per…...

学习华为熵减:激发组织活力(系列之三)
目录 为什么学习华为? 学习华为什么? 一、势:顺势而为,在风口上猪都会飞起来。 二、道:就是认识和利用规律层面,文化和制度创新就是企业经营之道。 三、法:就是一套价值管理的变革方法论。…...

CNCF云原生计算基金会
CNCF,全称为云原生计算基金会(Cloud Native Computing Foundation),成立于2015年,是一个隶属于Linux基金会的非营利组织。CNCF旨在促进和支持开源技术的发展,特别是那些支持云原生(cloud native…...

STM32 FreeRTOS时间片调度---FreeRTOS任务相关API函数---FreeRTOS时间管理
目录 时间片调度简介 FreeRTOS任务相关API函数介绍 延时函数介绍 时间片调度简介 在FreeRTOS中,同等优先级的任务会轮流分享相同的CPU时间,这个时间被称为时间片。在这里,一个时间片的长度等同于SysTick中断的周期。 FreeRTOS任务相关API…...

SQL LAST()
SQL中的LAST()函数是一个用于返回指定列中最后一个记录值的函数。然而,需要注意的是,这个函数并不是SQL标准的一部分,因此并不是所有数据库系统都支持它。具体来说,只有MS Access直接支持LAST()函数【0†source】。 在其他数据库…...

云服务信息安全管理体系认证,守护云端安全
在数据驱动的时代,云计算已成为企业业务的超级引擎,推动着企业飞速发展。然而,随着云计算的广泛应用,信息安全问题也日益凸显,如同暗流涌动下的礁石,时刻威胁着企业的航行安全。这时,云服务信息…...

【Hive】新增字段(column)后,旧分区无法更新数据问题
TOC 【一】问题描述 Hive修改数据表结构的需求,比如:增加一个新字段。 如果使用如下语句新增列,可以成功添加列col1。但如果数据表tb已经有旧的分区(例如:dt20190101),则该旧分区中的col1将为…...

C# (图文教学)在C#的编译工具Visual Studio中使用SQLServer并对数据库中的表进行简单的增删改查--14
目录 一.安装SQLServer 二.在SQLServer中创建一个数据库 1.打开SQL Server Manager Studio(SSMS)连接服务器 2.创建新的数据库 3.创建表 三.Visual Studio 配置 1.创建一个简单的VS项目(本文创建为一个简单的控制台项目) 2.添加数据库连接 四.简单连通代码示例 简单连…...

4G DTU赋能智能配电环网柜通信运维管理
在智能电网建设持续推进下,智能配电环网柜作为配电网的关键节点设备,其稳定、高效运行对保障电力可靠供应是品质生活的基本保障。通信系统是实现智能配电环网柜远程监控与管理的核心纽带,而4G DTU(数据传输单元)凭借其…...

快速上手 HarmonyOS 应用开发
一、DevEco Studio 安装与配置 1. DevEco Studio 简介 DevEco Studio 是 HarmonyOS 的一站式集成开发环境(IDE),提供了丰富的工具和功能,支持 HarmonyOS 应用开发的全流程。 2. DevEco Studio 下载与安装 下载地址:…...

解决nginx: [emerg] unknown directive “stream“ in /etc/nginx/nginx.conf问题 --九五小庞
参考链接:https://blog.csdn.net/User_bie/article/details/128102168 Nginx在编译安装时,需指定安装的模块...

Object类和hashcode方法
一.Object类. 1.所有类的父类,如果一个类没有继承那个类,那么都是默认继承于Object这个类。 2.object中存在的方法:(画横线的方法代表过时了,但是能用) 3.这里能用equals的方法是因为默认继承了Object这个…...

速通Docker === 介绍与安装
目录 Docker介绍 Docker优势 Docker组件 Docker CLI (命令行接口) Docker Host (Docker 守护进程) 容器 (Container) 镜像 (Image) 仓库 (Registry) 关系总结 应用程序部署方式 传统部署 (Traditional Deployment) 虚拟化部署 (Virtualization Deployment) 容器部署…...

OpenStack 网络服务的插件架构
OpenStack 的网络服务具有灵活的插件架构,可支持多种不同类型的插件以满足不同的网络需求。以下是对 OpenStack 网络服务插件架构中一些常见插件类型的介绍: 一、SDN 插件 Neutron 与 SDN 的集成:在 OpenStack 网络服务里,SDN 插…...

SAP SD销售模块组织架构介绍(销售组织、分销渠道等)
【SAP系统研究】 #SAP #SD #销售管理 #销售组织 一、销售组织 Sales Organization,是为企业销售而建立的组织单元,用于物料和服务的销售。销售组织下又可以分为若干个分销渠道,用于不同的市场服务;分销渠道又可以按产品组的不同…...

CMake学习笔记(2)
1. 嵌套的CMake 如果项目很大,或者项目中有很多的源码目录,在通过CMake管理项目的时候如果只使用一个CMakeLists.txt,那么这个文件相对会比较复杂,有一种化繁为简的方式就是给每个源码目录都添加一个CMakeLists.txt文件ÿ…...

Linux下源码编译安装Nginx1.24及服务脚本实战
1、下载Nginx [rootlocalhost ~]# wget -c https://nginx.org/download/nginx-1.24.0.tar.gz2、解压 [rootlocalhost ~]# tar xf nginx-1.24.0.tar.gz -C /usr/local/src/3、安装依赖 [rootlocalhost ~]# yum install gcc gcc-c make pcre-devel openssl-devel -y4、 准备 N…...

4、dockerfile实现lnmp和elk
dockerfile实现lnmp 使用dockerfile n:nginx,172.111.0.10 m:mysql,172.111.0.20 p:php,172.111.0.30 安装配置nginx 1、准备好nginx和wordpress安装包 2、配置dockerfile 3、配置nginx主配置文件ngin…...