news 2026/6/10 17:52:18

Java中的锁机制总结

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java中的锁机制总结

文章目录

    • 一、锁的基本概念:为什么需要锁?
    • 二、锁的分类体系
    • 三、核心锁机制深度剖析
      • 1. 悲观锁 vs 乐观锁
      • 2. synchronized的锁升级过程
      • 3. 读写锁(ReadWriteLock)
      • 4. 自旋锁
    • 四、数据库锁机制
      • 1. 行锁 vs 表锁
      • 2. 间隙锁(Gap Lock)与临键锁(Next-Key Lock)
      • 3. 意向锁
    • 五、分布式锁:超越单机范畴
      • 1. 基于Redis的分布式锁
      • 2. 基于Zookeeper的分布式锁
    • 六、实战案例:转账业务中的锁应用
    • 七、避坑指南与最佳实践
    • 八、总结
    • 参考文章

大家好,我是你们的技术老友科威舟,今天给大家分享一下Java中的锁机制。

技术干货满满,一文带你了解Java中的锁。

在并发编程的世界里,锁是保障线程安全的重要工具。但面对琳琅满目的锁机制,很多开发者都会感到困惑。今天,我们将深入剖析Java中的各种锁机制,带你打通并发编程的"任督二脉"。

一、锁的基本概念:为什么需要锁?

在多线程环境下,多个线程同时访问共享资源会导致数据竞争问题。锁作为一种同步机制,可以确保同一时间只有一个线程能够访问共享资源,从而保证数据的一致性。

可以把锁想象成卫生间钥匙:当一个使用者拿到钥匙进入卫生间时,其他人必须等待;使用完毕后归还钥匙,下一个人才能使用。这种机制避免了尴尬的"撞车"情况。

二、锁的分类体系

Java中的锁可以按照不同的维度进行分类,让我们通过一个表格全面了解锁的家族体系:

分类维度锁类型典型实现特点
锁的性质悲观锁synchronized, ReentrantLock认为会发生并发冲突,先加锁再访问
乐观锁CAS, AtomicInteger认为不会发生冲突,先修改再检查
锁的粒度表锁数据库表锁锁定整张表,粒度粗
行锁数据库行锁锁定特定行,粒度细
读写特性读写锁ReentrantReadWriteLock读共享,写独占
排它锁synchronized, ReentrantLock完全独占
锁状态偏向锁synchronized优化偏向第一个访问线程
轻量级锁synchronized优化CAS实现,避免线程阻塞
重量级锁synchronized最终形态基于操作系统互斥量
等待机制自旋锁CAS自旋循环尝试获取锁
范围意向锁数据库意向锁表明"意图"加行级锁

三、核心锁机制深度剖析

1. 悲观锁 vs 乐观锁

悲观锁就像一位过度谨慎的管家,总是假设最坏情况——认为每次访问共享资源时都会有其他线程修改数据。因此,它在操作前总会先加锁。

// 悲观锁示例 - synchronizedpublicsynchronizedvoidtransferMoney(Accountfrom,Accountto,doubleamount){// 转账逻辑from.withdraw(amount);to.deposit(amount);}

乐观锁则像一位乐观的协调者,假设操作期间不会发生冲突,只在提交时检查数据是否被修改。

// 乐观锁示例 - CASpublicclassOptimisticLockExample{privateAtomicIntegerbalance=newAtomicInteger(100);publicbooleanwithdraw(intamount){intcurrent;do{current=balance.get();if(current<amount){returnfalse;}}while(!balance.compareAndSet(current,current-amount));returntrue;}}

适用场景:悲观锁适合写多读少的高冲突场景;乐观锁适合读多写少的低冲突场景。

2. synchronized的锁升级过程

synchronized的优化过程就像职场新人的成长路径

偏向锁(新人期):当只有一个线程访问时,JVM会偏向这个线程,后续访问无需同步操作。这就像部门里只有一个员工,所有资源都优先给他使用。

轻量级锁(成长期):当有轻微竞争时,线程通过CAS操作尝试获取锁,避免线程阻塞。如同几个同事协作,通过简单沟通就能解决问题。

重量级锁(成熟期):当竞争激烈时,锁升级为重量级锁,未获取到锁的线程会被阻塞。这就像重要项目需要正式会议和严格流程来协调资源。

3. 读写锁(ReadWriteLock)

读写锁采用了"多读单写"的策略,非常适合读多写少的场景。

publicclassReadWriteCache{privateMap<String,Object>cache=newHashMap<>();privateReadWriteLockrwLock=newReentrantReadWriteLock();publicObjectget(Stringkey){rwLock.readLock().lock();try{returncache.get(key);}finally{rwLock.readLock().unlock();}}publicvoidput(Stringkey,Objectvalue){rwLock.writeLock().lock();try{cache.put(key,value);}finally{rwLock.writeLock().unlock();}}}

这种设计允许多个读线程同时访问,但写线程独占访问,大大提高了系统的并发性能。

4. 自旋锁

自旋锁的理念是:如果锁的持有时间很短,那么等待锁的线程稍作循环等待(自旋)可能比挂起更高效

就像等电梯时,如果知道电梯马上就到,我们会选择在门口等待(自旋)而不是回工位(线程挂起)。

publicclassSpinLock{privateAtomicBooleanlocked=newAtomicBoolean(false);publicvoidlock(){// 自旋等待while(!locked.compareAndSet(false,true)){// 自旋期间可以执行一些轻量级操作}}publicvoidunlock(){locked.set(false);}}

但自旋锁要注意自旋时间,过长会浪费CPU资源。

四、数据库锁机制

1. 行锁 vs 表锁

行锁就像精确制导导弹,只锁定需要的数据行,其他行仍可正常访问。这提供了更好的并发性,但管理开销较大。

表锁则是范围轰炸,直接锁定整张表,管理简单但并发性差。

实战场景:在用户余额更新时使用行锁,避免全表锁定影响其他用户操作。

2. 间隙锁(Gap Lock)与临键锁(Next-Key Lock)

间隙锁锁定索引记录之间的间隔,防止幻读;临键锁则是行锁+间隙锁的组合。

比如,防止在事务执行过程中,有其他插入操作在条件范围内添加新记录。

3. 意向锁

意向锁是一种声明式锁,表示"我打算在这个资源上加更细粒度的锁"。这就像在会议室门口挂"预定"牌子,表明后续会详细使用。

五、分布式锁:超越单机范畴

在分布式系统中,单机锁已无法满足需求,我们需要分布式锁来协调多个节点。

1. 基于Redis的分布式锁

publicclassRedisDistributedLock{publicbooleantryLock(StringlockKey,StringrequestId,intexpireTime){returnredisTemplate.opsForValue().setIfAbsent(lockKey,requestId,expireTime,TimeUnit.SECONDS);}publicbooleanunlock(StringlockKey,StringrequestId){// 使用Lua脚本保证原子性Stringscript="if redis.call('get', KEYS[1]) == ARGV[1] then "+"return redis.call('del', KEYS[1]) else return 0 end";returnredisTemplate.execute(newDefaultRedisScript<Long>(script,Long.class),Collections.singletonList(lockKey),requestId)==1;}}

2. 基于Zookeeper的分布式锁

Zookeeper通过创建临时顺序节点实现公平的分布式锁,像银行排队机一样保证先来后到。

六、实战案例:转账业务中的锁应用

让我们通过一个完整的转账案例,展示不同锁的应用:

@ServicepublicclassTransferService{// 使用ReentrantLock保证账户操作的原子性privatefinalLocklock=newReentrantLock(true);// 公平锁@Transactionalpublicbooleantransfer(Accountfrom,Accountto,BigDecimalamount){if(lock.tryLock()){try{// 检查余额是否充足if(from.getBalance().compareTo(amount)>=0){from.debit(amount);to.credit(amount);accountRepository.save(from);accountRepository.save(to);returntrue;}}finally{lock.unlock();}}returnfalse;}}

在这个案例中,我们结合了数据库事务ReentrantLock余额检查,确保了转账操作的安全性和一致性。

七、避坑指南与最佳实践

  1. 死锁预防:避免嵌套锁,按固定顺序获取锁,使用定时锁
  2. 锁粒度:尽量减小锁的粒度,只锁定必要的代码块
  3. 锁分离:读写分离,使用ReadWriteLock替代独占锁
  4. 性能监控:关注锁竞争情况,避免不必要的锁升级

八、总结

Java的锁机制是一个庞大而精妙的体系,从简单的synchronized到复杂的分布式锁,每种锁都有其适用场景。合理选择和使用锁,是构建高并发应用的关键。

记住:没有最好的锁,只有最合适的锁。明智地选择锁策略,让你的应用在并发场景下游刃有余!

参考文章

  1. https://developer.aliyun.com/article/1441144
  2. https://blog.csdn.net/weixin_44817884/article/details/136677898
  3. https://blog.csdn.net/u013738122/article/details/105474728
  4. https://m.php.cn/zh-tw/faq/556205.html
  5. https://docs.pingcode.com/baike/275423
  6. https://docs.pingcode.com/baike/390005
  7. https://blog.51cto.com/u_16175498/13407976

希望这篇文章能帮助你更好地理解Java中的各种锁机制!如有疑问,欢迎留言讨论。


更多技术干货欢迎关注微信公众号科威舟的AI笔记~

【转载须知】:转载请注明原文出处及作者信息

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 15:03:15

PocoEmit遥遥领先于AutoMapper之打通充血模型的任督二脉

一、充血模型和失血模型1. 充血模型的优势充血模型更加OOP充血模型代码可读性更好1.1 充血模型伪代码var messageDto controller.ReadDto();var message messageDto.ToEntity();message.Save();1.2 失血模型伪代码var messageDto controller.ReadDto();var message message…

作者头像 李华
网站建设 2026/6/10 14:39:37

基于改进蛇优化算法(GOSO/ISO)优化BP神经网络的数据回归预测探索

基于改进蛇优化算法(GOSO/ISO)优化BP神经网络的数据回归预测(GOSO/ISO-BP) 蛇优化算法SO是2022年提出的新算法&#xff0c;性能优异&#xff0c;目前应用较少&#xff0c;改进蛇优化算法GOSO/ISO应用更少&#xff0c;适合PAPER 改进点1为在初始化种群引入混沌映射&#xff0c;本…

作者头像 李华
网站建设 2026/6/10 15:42:22

数据结构总结笔记

1 数据结构三要素是什么&#xff1f;逻辑结构包括什么&#xff1f;存储结构包括什么&#xff1f; 数据结构三要素&#xff1a;逻辑结构、存储结构、数据运算。 逻辑结构包括线性结构和非线性结构&#xff1a; 线性结构&#xff1a;线性表、栈、队列&#xff1b;非线性结构&a…

作者头像 李华
网站建设 2026/6/10 15:36:01

PHP 开发者必须掌握的基本 Linux 命令

开始 PHP 开发时&#xff0c;我并没有特别的理由选择 Linux。既不是出于对开源软件的热情&#xff0c;也不是认为 Linux 比 macOS 或 Windows 更优秀。当时只是希望找到一个更轻量、更可定制的开发环境。对我而言&#xff0c;Linux 就是一个可能提升编码效率的工具。随着项目规…

作者头像 李华
网站建设 2026/6/9 17:42:18

用格子玻尔兹曼方法(LBM)模拟不可压缩密度守恒压力驱动流

格子玻尔兹曼方法&#xff08;LBM&#xff09;模拟不可压缩密度守恒压力驱动流&#xff0c;MATLAB代码嘿&#xff0c;大家好&#xff01;今天来聊聊如何使用格子玻尔兹曼方法&#xff08;LBM&#xff09;模拟不可压缩密度守恒压力驱动流&#xff0c;并且会用MATLAB代码来实现。…

作者头像 李华
网站建设 2026/6/10 15:30:24

基于LQR控制算法的四轮独立电驱动汽车横向稳定性控制研究

适用方向&#xff1a;基于LQR控制算法的直接横摆力矩控制&#xff08;DYC&#xff09;的四轮独立电驱动汽车的横向稳定性控制研究 主要内容&#xff1a;利用carsim建模&#xff0c;在simulink中搭建控制器&#xff0c;然后进行联合。 实现汽车在高速低附着路面下完成双移线工况…

作者头像 李华