Java 中常见的锁与最佳实践

 更新时间:2026年03月11日 08:57:34   作者:数据  
本文介绍了Java中常见的锁,包括语法/实现层面的synchronized关键字、Lock接口及其实现类,讨论了锁的特性、常见误区与最佳实践,感兴趣的朋友跟随小编一起看看吧

Java 中的锁可以从多个维度来划分,常见的锁包括:

  • 语法/实现层面:synchronized 关键字(内置锁)、Lock 接口及其实现类(如 ReentrantLockReentrantReadWriteLockStampedLock)。
  • 特性层面:可重入锁、公平锁/非公平锁、读写锁、乐观锁/悲观锁、自旋锁、分段锁等。
  • 并发工具包中的辅助锁:Semaphore(信号量,可看作共享锁)、CountDownLatchCyclicBarrier 等虽不是严格意义上的锁,但也用于控制线程协作。

下面我们从最常用的几个维度展开聊聊。

1. 从语法和 API 层面看

synchronized(内置锁)

  • 使用方式:修饰实例方法、静态方法或代码块。
  • 原理:基于对象头的 Mark Word 和 monitor 对象实现。JDK 1.6 以后引入了偏向锁、轻量级锁、重量级锁的升级过程,以优化性能。
  • 特点:使用简单,自动释放锁(异常或正常退出时),可重入。但无法中断等待锁的线程,也无法设置超时。
  • 最佳实践:适用于锁的竞争不激烈、代码简单的情况。比如单例模式的同步方法、简单的线程安全计数器。

Lock接口(显式锁)

最核心的实现是 ReentrantLock,它提供了比 synchronized 更灵活的锁操作。

  • ReentrantLock
  • 示例:
ReentrantLock lock = new ReentrantLock(true); // 公平锁
lock.lock();
try {
    // 临界区
} finally {
    lock.unlock(); // 必须手动释放
}
  • 可重入:同一个线程可以多次获得同一把锁。
  • 公平/非公平:构造函数可指定是否公平。公平锁按线程等待顺序获取锁,非公平锁允许插队(默认非公平,吞吐量更高)。
  • 可中断:lockInterruptibly() 允许在等待锁时响应中断。
  • 超时获取:tryLock(long time, TimeUnit unit) 可以指定等待时间。
  • 配合 Condition:可实现多个等待/通知条件,比 wait/notify 更精细。
  • ReentrantReadWriteLock
  • 维护一对锁:读锁(共享)和写锁(排他)。读多写少场景下可大幅提升并发度。
  • 读锁可以被多个读线程同时持有,但写锁独占,且读写互斥。
  • 可能出现“写饥饿”问题,但在非公平模式下,写锁优先级通常较高。
  • StampedLock(JDK 8 引入)
  • 进一步优化读多写少场景,支持三种模式:写锁、读锁、乐观读。
  • 乐观读:不加锁,直接读取数据,之后通过 validate 检查是否有写操作发生。若无效再升级为读锁重读。
  • 特点:不可重入,不支持条件变量,适合读线程远多于写线程的场景。

2. 从锁的特性层面看

  • 悲观锁 vs 乐观锁
    • 悲观锁:假设并发冲突概率高,每次操作都加锁(如 synchronized、ReentrantLock)。适合写多场景。
    • 乐观锁:假设冲突少,先操作,提交时用 CAS(Compare And Swap)检测冲突,冲突则重试。如 java.util.concurrent.atomic 包下的原子类。适合读多写少、冲突概率低的场景。
  • 可重入锁
    • 同一线程可以重复获取同一把锁,避免死锁。Java 中的 synchronized 和 ReentrantLock 都是可重入的。
  • 公平锁 vs 非公平锁
    • 公平锁按线程请求锁的顺序获取锁,避免饥饿,但吞吐量较低;非公平锁允许插队,吞吐量更高,但可能导致某些线程长时间得不到锁。
  • 自旋锁
    • 线程获取锁失败时不立即挂起,而是循环尝试(自旋),减少线程上下文切换的开销。适合锁持有时间短的场景。JDK 内部大量使用了自旋锁(如 ConcurrentLinkedQueue 的入队操作)。Java 中也提供了 AbstractQueuedSynchronizer(AQS)对自旋和阻塞的支持。
  • 分段锁
    • ConcurrentHashMap 在 JDK 1.7 中的实现,将数据分段,每段一把锁,降低锁粒度,提升并发度。JDK 1.8 改用 CAS + synchronized 对每个桶(数组元素)加锁,相当于进一步细化。

3. 并发工具包中的 “锁” 变体

  • Semaphore:计数信号量,可以控制同时访问资源的线程数量(共享锁)。比如限流场景。
  • CountDownLatch:让一个或多个线程等待,直到一组操作完成。
  • CyclicBarrier:让一组线程互相等待,到达某个屏障点后再同时执行。

虽然这些不是传统意义上的锁,但它们也常被用来解决线程协作问题,面试时可以作为补充提及。

常见误区与最佳实践

  • 误区一:认为 synchronized 性能总比 ReentrantLock 差。实际上 JDK 1.6 以后 synchronized 经过优化,性能与 ReentrantLock 差距不大,甚至在低竞争时更好。选择时应更关注功能需求。
  • 误区二:滥用读写锁。如果读操作很短,或者写操作频繁,读写锁可能不如普通互斥锁,因为维护读锁和写锁本身也有开销。
  • 误区三:忘记在 finally 中释放显式锁。这是新手最容易犯的错误,可能导致死锁。
  • 最佳实践:
    • 优先使用 synchronized,简洁安全;需要高级功能(如可中断、超时、公平性、多 Condition)时才选用 ReentrantLock。
    • 读多写少且数据一致性要求不那么极致时,可考虑 StampedLock 的乐观读模式,或使用 CopyOnWrite 容器。
    • 对于简单的原子操作,优先使用 Atomic 系列类(乐观锁思想),避免锁开销。
    • 了解 JVM 的锁优化(如偏向锁、轻量级锁),有助于调优高并发应用。

到此这篇关于Java 中常见的锁有哪些?的文章就介绍到这了,更多相关java常见锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot实现事件监听(异步执行)的示例代码

    SpringBoot实现事件监听(异步执行)的示例代码

    事件监听是一种机制,可以定义和触发自定义的事件,以及在应用程序中注册监听器来响应这些事件,本文主要介绍了SpringBoot实现事件监听(异步执行)的示例代码,感兴趣的可以了解一下
    2024-08-08
  • 浅谈myBatis中的插件机制

    浅谈myBatis中的插件机制

    这篇文章主要介绍了浅谈myBatis中的插件机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • javaweb用户注销后点击浏览器返回刷新页面重复登录问题的解决方法

    javaweb用户注销后点击浏览器返回刷新页面重复登录问题的解决方法

    这篇文章主要为大家详细介绍了javaweb用户注销后点击浏览器返回刷新页面重复登录问题的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • 聊聊SpringBoot自动装配的魔力

    聊聊SpringBoot自动装配的魔力

    这篇文章主要介绍了SpringBoot自动装配的魔力,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • 利用Spring boot+LogBack+MDC实现链路追踪

    利用Spring boot+LogBack+MDC实现链路追踪

    这篇文章主要介绍了利用Spring boot+LogBack+MDC实现链路追踪,MDC 可以看成是一个与当前线程绑定的哈希表,可以往其中添加键值对,下文详细介绍需要的小伙伴可以参考一下
    2022-04-04
  • 全面解析SpringBoot文件上传功能

    全面解析SpringBoot文件上传功能

    这篇文章主要为大家全面解析SpringBoot文件上传功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • java计算两点间的距离方法总结

    java计算两点间的距离方法总结

    小编给大家总结了在java中计算两点之家距离的方法以及相关实例代码分享,有需要的读者参考下。
    2018-02-02
  • myatisplus的saveOrUpdate的提交总是update问题

    myatisplus的saveOrUpdate的提交总是update问题

    这篇文章主要介绍了myatisplus的saveOrUpdate的提交总是update问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 使用springboot配置和占位符获取配置文件中的值

    使用springboot配置和占位符获取配置文件中的值

    这篇文章主要介绍了使用springboot配置和占位符获取配置文件中的值,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • MyBatis Mapper XML中比较操作符转义问题解决

    MyBatis Mapper XML中比较操作符转义问题解决

    在使用MyBatis编写Mapper XML时,有时会遇到比较操作符需要进行转义的情况,本文主要介绍了MyBatis Mapper XML中比较操作符转义问题解决,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01

最新评论