当前位置: 首页 > news >正文

多线程锁

在并发编程中,锁(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);}}
}

结语

感谢您的阅读!如果您对多线程锁或其他并发编程话题有任何疑问或见解,欢迎继续探讨。

相关文章:

多线程锁

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

ZooKeeper 核心知识全解析:架构、角色、节点与应用

1.ZooKeeper 分布式锁怎么实现的 ZooKeeper 是一个高效的分布式协调服务&#xff0c;它提供了简单的原语集来构建更复杂的同步原语和协调数据结构。利用 ZooKeeper 实现分布式锁主要依赖于它的顺序节点&#xff08;Sequential Node&#xff09;特性以及临时节点&#xff08;Ep…...

笔记本电脑 选购 回收 特权模式使用 指南

笔记本电脑 factor 无线网卡&#xff1a;有些笔记本无法检测到特定频段的信息&#xff0c;会导致连不上校园网 sudo iwlist wlp2s0 scan | grep Frequency > net.txt cat net.txt>表示用终端输出覆盖后续文件&#xff0c;>>表示添加到后续文件的末尾 一种更简…...

2023-2024 学年 广东省职业院校技能大赛(高职组)“信息安全管理与评估”赛题一

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

C#补充----反射,特性,迭代器,特殊语法,值类型运用类型。

1.反射&#xff1a;通过type 获取类中的数据。创建实例&#xff0c;并赋值。 《1》获取类的方式 《2》反射的应用 <1>获取类型的所有公共成员 <2>获取构造函数 <3>获取类型的 公共成员变量 <4>获取类型的 公共方法 <5>.获取类型的 属性 <6&g…...

深度学习核函数

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

Spring MVC流程一张图理解

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

计算机网络速成

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

spring.profiles.active不同优先级

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

我这不需要保留本地修改, 只需要拉取远程更改

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

源码编译安装httpd 2.4,提供系统服务管理脚本并测试(两种方法实现)

下载 httpd 2.4 源码&#xff1a; wget https://dlcdn.apache.org/httpd/httpd-2.4.x.tar.gztar -zxvf httpd-2.4.x.tar.gzcd httpd-2.4.x配置、编译和安装&#xff1a; ./configure --prefix/usr/local/apache2 --enable-so --enable-ssl --enable-cgi makesudo make install实…...

深度学习在自动化测试中的创新应用:提升运维效率与质量

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

单独编译QT子模块

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

学习华为熵减:激发组织活力(系列之三)

目录 为什么学习华为&#xff1f; 学习华为什么&#xff1f; 一、势&#xff1a;顺势而为&#xff0c;在风口上猪都会飞起来。 二、道&#xff1a;就是认识和利用规律层面&#xff0c;文化和制度创新就是企业经营之道。 三、法&#xff1a;就是一套价值管理的变革方法论。…...

CNCF云原生计算基金会

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

STM32 FreeRTOS时间片调度---FreeRTOS任务相关API函数---FreeRTOS时间管理

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

SQL LAST()

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

云服务信息安全管理体系认证,守护云端安全

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

【Hive】新增字段(column)后,旧分区无法更新数据问题

TOC 【一】问题描述 Hive修改数据表结构的需求&#xff0c;比如&#xff1a;增加一个新字段。 如果使用如下语句新增列&#xff0c;可以成功添加列col1。但如果数据表tb已经有旧的分区&#xff08;例如&#xff1a;dt20190101&#xff09;&#xff0c;则该旧分区中的col1将为…...

C# (图文教学)在C#的编译工具Visual Studio中使用SQLServer并对数据库中的表进行简单的增删改查--14

目录 一.安装SQLServer 二.在SQLServer中创建一个数据库 1.打开SQL Server Manager Studio(SSMS)连接服务器 2.创建新的数据库 3.创建表 三.Visual Studio 配置 1.创建一个简单的VS项目(本文创建为一个简单的控制台项目) 2.添加数据库连接 四.简单连通代码示例 简单连…...

别再只改默认密码了!Nacos 1.x/2.x 生产环境安全加固保姆级清单(附漏洞自查脚本)

Nacos生产环境安全加固全指南&#xff1a;从基础配置到漏洞防御 在微服务架构盛行的今天&#xff0c;Nacos作为服务发现和配置管理的核心组件&#xff0c;其安全性直接影响整个系统的稳定性。许多团队在部署Nacos时往往只满足于修改默认密码&#xff0c;却忽视了完整的安全防护…...

忍者像素绘卷:天界画坊Python入门实战,3步搭建AI绘画环境

忍者像素绘卷&#xff1a;天界画坊Python入门实战&#xff0c;3步搭建AI绘画环境 1. 前言&#xff1a;当Python遇见像素艺术 还记得小时候玩过的8-bit游戏吗&#xff1f;那些由一个个小方块组成的像素世界&#xff0c;如今正以全新的方式回归。天界画坊是一个开源的AI绘画工具…...

从单片机思维到FPGA思维:我用Xilinx Ego1做循迹小车踩过的那些‘坑’

从单片机思维到FPGA思维&#xff1a;Xilinx Ego1循迹小车开发实战避坑指南 第一次用FPGA做循迹小车时&#xff0c;我盯着Vivado里密密麻麻的时序报告发呆了半小时——这和我熟悉的单片机开发完全是两个世界。作为有三年STM32开发经验的工程师&#xff0c;本以为凭借Verilog语法…...

车载Java OTA升级崩溃率从18.7%降至0.3%:基于Delta Patch + 类隔离热修复的4步标准化流程

第一章&#xff1a;车载Java OTA升级崩溃率从18.7%降至0.3%&#xff1a;基于Delta Patch 类隔离热修复的4步标准化流程在车载嵌入式Java环境&#xff08;JVM 11&#xff0c;ART兼容层&#xff09;中&#xff0c;OTA升级引发的ClassCastException与NoClassDefFoundError曾导致高…...

知乎上线求职工具,助力毕业生破困局

知乎上线求职利器&#xff0c;直击毕业生痛点2026届全国普通高校毕业生预计达1270万人&#xff0c;再创历史新高。与此同时&#xff0c;AI技术加速行业重构&#xff0c;部分传统岗位需求收缩&#xff0c;大量毕业生陷入“海投”困境&#xff0c;难以精准定位自身。在此背景下&a…...

Seqlist 顺序表 的实现c语言

本小结重点&#xff1a; 你将学到 函数基础 传值传地址的区别结构体指针 简单循环控制 理解物理结构与存储结构的区别多文件分布 简单来说就是对动态数组进行函数封装&#xff0c;简化了很多功能所以很多就是对数组的利用&#xff0c;但更多是对结构体数组&#xff0c;所…...

ComfyUI ControlNet模型与预处理器搭配秘籍:提升AI绘画精度的关键技巧

ComfyUI ControlNet模型与预处理器搭配秘籍&#xff1a;提升AI绘画精度的关键技巧 在AI绘画领域&#xff0c;ControlNet已经成为精细控制图像生成的重要工具。对于已经熟悉ComfyUI基础操作的用户来说&#xff0c;掌握ControlNet模型与预处理器的搭配技巧&#xff0c;是突破创作…...

深入解析STM32 map文件:从编译到内存优化的关键步骤

1. 为什么STM32开发者必须掌握map文件分析 第一次接触STM32的map文件时&#xff0c;我和大多数新手一样感到一头雾水。这个由编译器自动生成的文本文件&#xff0c;乍看就像天书般难以理解。直到有次项目遇到内存不足的紧急情况&#xff0c;我才真正体会到map文件的价值——它不…...

【深度解析】用 Superpowers 改造 AI 编码代理:从“快手实习生”到“有流程的工程师”

摘要 本文从 Superpowers 开源项目出发&#xff0c;拆解它背后的「AI 软件开发方法论」&#xff1a;如何通过技能&#xff08;Skills&#xff09;、子代理&#xff08;Sub-agents&#xff09;、规则&#xff08;Rules&#xff09;和 Git 工作流&#xff0c;让 AI 编码代理不再只…...

Mbed OS platform_drivers:嵌入式HAL驱动核心解析

1. 项目概述platform_drivers是 Arm Mbed OS 生态中一组经过严格验证、面向硬件抽象层&#xff08;HAL&#xff09;的平台级设备驱动集合&#xff0c;其核心定位并非提供通用外设封装&#xff0c;而是为 Mbed OS 内核及中间件组件提供可移植、可测试、符合 RTOS 语义的底层硬件…...