news 2026/10/27 6:38:47

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/10/26 11:44:20

JavaEE进阶——SpringBoot日志从入门到精通

目录 Spring Boot 日志详解&#xff1a;从入门到精通&#xff08;新手版&#xff09; 1. 日志概述&#xff1a;为什么要学&#xff1f; 1.1 从System.out.println到专业日志框架 2. 日志使用&#xff1a;手把手教你写代码 2.1 打印日志&#xff1a;第一个日志程序 知识点…

作者头像 李华
网站建设 2026/10/25 19:36:23

结构体简单题

1.这里主要是sort函数 配上自定义的函数&#xff08;题目要求同分的要求名字按ascll表从小到大排序&#xff09;还有就是vector数组再使用sort函数时不能使用&#xff08;arr&#xff0c;arrn&#xff09;关于sort函数&#xff1a;所以就是说你在return比较用了>就是按降序排…

作者头像 李华
网站建设 2026/10/27 13:09:17

飞轮储能系统的建模与 MATLAB 仿真:永磁同步电机作为飞轮驱动电机

飞轮储能系统的建模与MATLAB仿真&#xff08;永磁同步电机作为飞轮驱动电机&#xff09;不是模型嘿&#xff0c;各位技术爱好者&#xff01;今天咱们来聊聊飞轮储能系统的建模以及用 MATLAB 进行仿真的事儿&#xff0c;这里的飞轮驱动电机用的是永磁同步电机。飞轮储能系统可是…

作者头像 李华
网站建设 2026/10/27 14:43:24

车间进度总卡壳?生产小工单的3个必备功能,90%企业都用错了

你有没有过这种时刻&#xff1f;老板微信一问&#xff1a;进度到哪了&#xff1f;你只能回&#xff1a;正在做。干过车间的都懂&#xff0c;这哪是正在做&#xff0c;分明是不知道 。大部分的车间主管都这样&#xff1a;进度全靠猜&#xff0c;Excel堆满桌面&#xff0c;工人填…

作者头像 李华
网站建设 2026/10/25 15:12:14

如何用 ShedLock 让 Spring Boot 的定时任务在多实例环境下只执行一次

执行。原因很简单&#xff1a;默认情况下&#xff0c;Spring 不会在多个实例之间做调度同步。这篇文章就聊聊怎么用 ShedLock&#xff0c;让定时任务在多实例环境下“同一时刻只跑一次”。顺便一提&#xff0c;它也能作为 Quartz 的替代。Maven 依赖先引入 shedlock-spring 这个…

作者头像 李华