首页/新闻资讯/正文详情

Java 锁学习笔记

发布时间:2026/9/24 17:40:24 来源:云帆数科 栏目:资讯中心
Java 锁学习笔记
Java 锁学习笔记1. 为什么需要锁1.1 多线程为什么会出问题多个线程同时操作同一份共享数据时可能产生数据竞争Race Condition。例如privateintcount0;publicvoidadd(){count;}count看起来只有一行但实际上可以理解成读取 count ↓ count 1 ↓ 写回 count假设当前count 0两个线程同时执行线程 A读取 0 线程 B读取 0 线程 A0 1 → 写入 1 线程 B0 1 → 写入 1最终可能得到count 1但实际上执行了两次add()期望结果应该是2。这就是并发情况下的数据竞争。1.2 锁解决什么问题锁的核心作用让多个线程访问共享资源时按照一定规则进行协调。最简单的理解多个线程 ↓ 竞争同一把锁 ↓ 同一时间只能一个线程进入 ↓ 其他线程等待注意锁不是让整个 Java 程序变成单线程而是让竞争同一把锁的线程互斥。2. synchronized —— 同步锁2.1 什么是 synchronizedsynchronized是 Java 提供的同步机制。最简单理解同一把锁同一时间只能有一个线程进入受保护的代码。例如privateintcount0;publicsynchronizedvoidadd(){count;}多个线程同时调用时线程 A → 获得锁 → 执行 add() 线程 B → 等待 线程 C → 等待 A 执行结束 → 释放锁 B → 获得锁 → 执行2.2 synchronized 锁住的到底是什么这是理解synchronized最重要的一点synchronized 锁的是对象而不是代码本身。synchronized 实例方法publicsynchronizedvoidadd(){count;}可以近似理解为publicvoidadd(){synchronized(this){count;}}也就是说实例方法锁住的是当前对象this。synchronized 代码块privatefinalObjectlocknewObject();publicvoidadd(){synchronized(lock){count;}}这里锁的是lock对象。static synchronizedpublicstaticsynchronizedvoidtest(){}锁的是MyService.class2.3 什么情况下两个线程会互斥关键不是“是不是 synchronized 方法”而是是不是竞争同一把锁例如publicsynchronizedvoidadd(){}publicsynchronizedvoidsubtract(){}如果两个方法属于同一个对象那么add() → 锁 this subtract() → 锁 this所以它们会互斥。即使是两个不同的方法也需要排队。2.4 不同对象的 synchronized 不会互相阻塞例如MyServiceserviceAnewMyService();MyServiceserviceBnewMyService();此时serviceA.add() ↓ 锁 serviceA serviceB.add() ↓ 锁 serviceB两把锁不同所以可以同时执行。因此同一把锁 → 互斥 不同的锁 → 可以并发2.5 synchronized 的可重入synchronized本身就是可重入的。例如publicsynchronizedvoidmethodA(){methodB();}publicsynchronizedvoidmethodB(){}执行过程线程 A ↓ 进入 methodA() ↓ 获得 this 的锁 ↓ 调用 methodB() ↓ 再次获得 this 的锁 ↓ 正常执行不会把自己锁死。所以可重入 同一个线程已经持有一把锁时可以再次获得同一把锁。注意可重入不代表多个线程可以同时执行。2.6 synchronized 常见错误不要每次创建新的锁对象synchronized(newObject()){// ...}因为每次调用都会创建不同的对象线程之间没有竞争同一把锁。应该使用共享的锁对象privatefinalObjectlocknewObject();publicvoidtest(){synchronized(lock){// ...}}3. ReentrantLock —— 可重入锁3.1 什么是 ReentrantLockReentrantLock中文可重入锁它是java.util.concurrent.locks包中的一个具体锁实现。privatefinalReentrantLocklocknewReentrantLock();3.2 最基本的使用方式privatefinalReentrantLocklocknewReentrantLock();publicvoidadd(){lock.lock();try{count;}finally{lock.unlock();}}执行流程lock() ↓ 获取锁 ↓ 执行代码 ↓ unlock() ↓ 释放锁3.3 为什么必须 finally unlock因为ReentrantLock是手动管理锁。标准写法lock.lock();try{// 业务}finally{lock.unlock();}如果业务代码抛出异常而没有finally可能导致锁无法正常释放。3.4 synchronized 和 ReentrantLock 的区别synchronizedsynchronized(lock){// ...}Java 自动管理进入代码块 → 自动获得锁 离开代码块 → 自动释放锁ReentrantLocklock.lock();try{// ...}finally{lock.unlock();}程序员自己控制lock() ↓ 获得锁 unlock() ↓ 释放锁所以可以简单理解synchronized 更简单ReentrantLock 给你更多手动控制能力。3.5 tryLock()ReentrantLock一个重要能力lock.tryLock();意思是尝试获取锁拿不到就不一直等待。例如if(lock.tryLock()){try{// 执行任务}finally{lock.unlock();}}else{System.out.println(当前已经有人执行了);}区别lock() ↓ 拿不到 → 一直等 tryLock() ↓ 拿不到 → 返回 false还可以lock.tryLock(3,TimeUnit.SECONDS);表示最多等待 3 秒。3.6 ReentrantLock 也是可重入的同一个线程已经获得lock后可以再次获得同一把锁不会把自己锁死。4. ReentrantReadWriteLock —— 读写锁4.1 为什么需要读写锁假设 Java 内存中有一个共享缓存privateMapString,StringcachenewHashMap();大量线程读取cache.get(user);偶尔才修改cache.put(user,Tom);如果所有操作都使用普通互斥锁即使多个线程都只是读取也需要排队。读写锁就是解决这个问题的。4.2 基本写法通常这样定义privatefinalReadWriteLocklocknewReentrantReadWriteLock();这里ReadWriteLock ↓ 接口 ReentrantReadWriteLock ↓ 具体实现类似ListStringlistnewArrayList();4.3 读锁publicStringget(Stringkey){lock.readLock().lock();try{returncache.get(key);}finally{lock.readLock().unlock();}}多个线程可以同时获得读锁线程 A → 读锁 → 读取 线程 B → 读锁 → 读取 线程 C → 读锁 → 读取所以读 读可以并发。4.4 写锁publicvoidput(Stringkey,Stringvalue){lock.writeLock().lock();try{cache.put(key,value);}finally{lock.writeLock().unlock();}}写锁更加严格读 写 → 互斥 写 写 → 互斥总结读锁 写锁 读锁 ✅ ❌ 写锁 ❌ ❌4.5 ReentrantReadWriteLock 也是可重入的ReentrantReadWriteLock也是可重入锁。例如同一个线程持有写锁后可以再次获得同一个写锁。因此synchronized ReentrantLock ReentrantReadWriteLock都支持可重入。5. StampedLock —— 邮戳锁5.1 StampedLock 是什么StampedLock提供读锁 写锁 乐观读其中最重要的特色是乐观读Optimistic Read。5.2 什么是乐观读普通读锁我要读 ↓ 先获得读锁 ↓ 读取数据 ↓ 释放读锁乐观读我要读 ↓ 先不真正加读锁 ↓ 直接读取 ↓ 最后检查期间有没有发生写操作也就是我乐观地认为读取过程中不会有人修改数据。5.3 基本代码privateintx10;privateinty20;privatefinalStampedLocklocknewStampedLock();publicintgetTotal(){longstamplock.tryOptimisticRead();intcurrentXx;intcurrentYy;if(!lock.validate(stamp)){stamplock.readLock();try{currentXx;currentYy;}finally{lock.unlockRead(stamp);}}returncurrentXcurrentY;}不用死记代码重点理解流程开始读取 ↓ 乐观读取 ↓ 检查读取期间有没有写操作 ↓ 没有 → 读取结果有效 有 → 重新获得读锁 → 重新读取5.4 为什么叫“乐观”因为它采用的是一种乐观思想我认为冲突大概率不会发生所以先操作最后再检查。5.5 乐观读和乐观锁两者思想很接近乐观读 ↓ 先读 ↓ 最后验证 ↓ 发现冲突 → 重新读数据库中的乐观锁也常采用类似思路先读取数据 ↓ 记录 version ↓ 执行更新 ↓ 检查 version 是否变化 ↓ 没有变化 → 成功 发生变化 → 冲突因此乐观读采用了和乐观锁类似的并发控制思想但两者不是同一个机制。5.6 StampedLock 不可重入前面几个synchronized ReentrantLock ReentrantReadWriteLock都是可重入的。但是StampedLock 不支持可重入。这一点需要特别记住。6. 乐观锁 vs 悲观锁6.1 悲观锁核心思想我认为冲突很可能发生所以先把资源锁住再操作。流程先锁 ↓ 执行操作 ↓ 释放锁例如synchronized(lock){// 修改共享数据}线程线程 A → 拿到锁 → 执行 线程 B → 拿不到 → 等待这体现了典型的悲观思想“我认为别人可能会同时操作所以我先把门锁起来。”6.2 乐观锁核心思想我认为冲突不一定发生所以先操作最后检查有没有冲突。流程先操作 ↓ 最后检查 ↓ 没有冲突 → 成功 发生冲突 → 失败 / 重试 / 重新读取常见实现思路是版本号version 1更新时UPDATEaccountSETbalance900,versionversion1WHEREid1ANDversion1;如果其他线程已经把version 1改成version 2那么这次更新就不会成功。6.3 悲观锁 vs 乐观锁悲观锁乐观锁核心思想先锁住先操作冲突处理事前阻止事后检查冲突发生时等待失败 / 重试Java 中的典型体现synchronized、ReentrantLockCAS、版本号等思想数据库例子FOR UPDATEversion 字段6.4 “悲观锁”和“乐观锁”不是具体 Java 类这一点非常重要。Java 中没有PessimisticLock这样的标准锁类。“悲观锁”和“乐观锁”描述的是一种并发控制思想。例如synchronized ↓ Java 同步机制 ↓ 体现悲观并发控制思想ReentrantLock ↓ Java 可重入锁 ↓ 体现悲观并发控制思想而StampedLock 的乐观读 ↓ 体现乐观并发控制思想数据库也同样如此SELECT ... FOR UPDATE ↓ 悲观锁思想version / CAS ↓ 乐观锁思想

相关推荐

AI小白必看:轻松搞懂LLM、Chatbot和Agent
AI小白必看:轻松搞懂LLM、Chatbot和Agent

本文深入浅出地解析了AI领域中的三个核心概念:LLM、Chatbot和Agent。LLM是理解和生成内容的底层模型,相当于AI的“发动机”;Chatbot是基于LLM的对话产品,提供持续交互的界面;Agent则是在Chatbot基础上,能够… · 2026/9/24 17:40:12

小白/程序员必看:大模型+智能体如何赋能制造业,小白也能轻松入门!
小白/程序员必看:大模型+智能体如何赋能制造业,小白也能轻松入门!

本文介绍了“大模型智能体”在制造业中的应用,通过真实案例展示了其在流程工业、装备制造、电子制造等场景的显著效益,包括提升效率、降低成本、提高产品质量等。文章还分析了制造业AI落地的三大死结,以及“双轮驱动”如何破解这些难题&#… · 2026/9/24 17:40:12

AI时代大学专业重构:小白程序员如何抓住大模型机遇,抢占高薪饭碗?
AI时代大学专业重构:小白程序员如何抓住大模型机遇,抢占高薪饭碗?

文章指出高校专业正在因AI发展进行大洗牌,撤销1.22万个布点,新增1.02万个,特别是AI专业布点激增406个。AI时代,信息管理与信息系统等专业因课程陈旧被大量撤销,而AI相关岗位需求旺盛,应届AI工程师平均年薪约… · 2026/9/24 17:40:12

GTA5增强版460报错实测:驱动、DX11与虚拟内存方案对比
GTA5增强版460报错实测:驱动、DX11与虚拟内存方案对比

玩GTA5的兄弟应该都懂,最近更新“增强版”之后,论坛里冒出来一堆报错帖,最常见的一个就是“460”。这个错误在启动时突然弹出来,游戏刚看到LOGO就黑屏闪退,运气好一点能进去玩十分钟,然后画面一卡&#xff… · 2026/9/24 18:16:01

R星启动器连不上服务器报错460?4种实测修复方法详解
R星启动器连不上服务器报错460?4种实测修复方法详解

估计很多朋友碰到过这种事:游戏里刚打完一局准备回线下,或者清晨打开Steam点开始,R星启动器转两圈,直接弹出一句“无法连接Rockstar Games服务(代码460)”。我第一次遇到时也天真地以为是校园网抽风&#x… · 2026/9/24 18:16:01

AI提效实战:DeepSeek、Kimi、Cursor三款工具组合使用指南
AI提效实战:DeepSeek、Kimi、Cursor三款工具组合使用指南

每天被各类消息、文档、报表轮番轰炸,加班到八九点成了不少职场人的日常。我大概花了三周时间,把市面上能叫得出名字的AI工具挨个用了一遍,最后真正留在工作流里、每天都离不开的其实只有三款。它们不是那种“看起来很酷但用不上”的玩具&… · 2026/9/24 18:16:01

Windows 开机自动录屏 8 种方案详解:从任务计划程序到 FFmpeg 实战
Windows 开机自动录屏 8 种方案详解:从任务计划程序到 FFmpeg 实战

Windows 上实现开机自动录屏,听上去是个很小的需求,真做起来却有一堆讲究。我最近因为要给一台无人值守的机器做操作记录,把 Win10 22H2 和 Win11 23H2 上能用的开机自动录屏方案挨个测了一遍,发现最大的坑不是"找不到方法&q… · 2026/9/24 18:16:01

Python波束形成仿真:从环境配置到算法调试实战指南
Python波束形成仿真:从环境配置到算法调试实战指南

简介:本资源是一套基于Python的波束形成算法仿真代码与可视化结果集,面向信号处理、雷达通信及阵列天线方向的高校学生、科研人员与工程师,用于深入理解并实践主流波束形成技术原理与性能对比。压缩包共114个文件,含23个核心Pytho… · 2026/9/24 18:16:01

朴素贝叶斯实战:真假新闻分类模型从训练到部署
朴素贝叶斯实战:真假新闻分类模型从训练到部署

简介:本资源面向数据挖掘与自然语言处理初学者及进阶学习者,围绕朴素贝叶斯算法构建真假新闻分类模型这一典型文本分类任务,提供从数据准备到模型评估的完整实战方案。压缩包共4个文件,约27.2MB,包含Fake.csv与True.cs… · 2026/9/24 18:15:55

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码