JAVA锁的理解

synchronized和reactional的区别。
先从jvm说起,synchronized是jvm内置的锁,不用手动加锁释放,哪怕代码抛异常,它会自动的释放锁,加在实例方法上,锁的是当前对象,加在静态方法上,锁的是类的class对象,加在代码块上,锁的就是指定的对象,原理很简单,靠对象头里的markword记录锁的状态。
锁膨胀,第一个线程来访问,直接分配偏向锁,畅通无阻,第二个对象来抢锁,偏向锁就升级为轻量级锁,靠CAS自旋等待,自旋多次还拿不到锁,才会升级成为重量级锁,线程阻塞进入内核态,这个过程锁膨胀是单向的,只会升级,不会降级,再看这1k,也就是lock接口的体系,最常用的就是reentrantlock,它和synchronized最核心的差别就是灵活性,你可以动动控制加锁,和释放锁的一个时机,可以设置超时时间,拿不到锁就直接放弃,还能响应中断,甚至可以绑定多个condition,实现精准的线程唤醒,这里有两个核心的概念,第一个是可重入,同一个线程每加一次锁,计数器就加一,每释放一次就减一,直到计数器归零,锁才会真正的被释放,第二个就是公平锁和非公平锁,非公平锁上来就直接抢,整体吞吐量比较高,但是可能出现线程饥饿,有的线程一直抢不到,公平锁严格按照排队顺序来,不会有线程挨饿,但整体效率比较低的,reentrantlock默认是非公平锁,构造方法里传了一个true,就会变成公平锁,还有就是读写锁也必须掌握,代表就是reentrant的read write lock,规则很简单,读与写共享不互斥,读与写互斥,写与写也互斥,像缓存这种读多写少的一个场景,用它就再合适不过,锁选型的标准,普通简单的场景,synchronized就完全够用了,灵活需要灵活控制锁的,用reactionLock,读多写少的场景,直接上读写锁,高并发场景,再考虑CAS加原子类的或者ConcurrentHashMap这内部已经做好细粒度锁的并发容器。

AI回答:synchronized 和 ReentrantLock 都是 Java 中独占式的可重入锁,核心作用都是保证多线程场景下的共享资源安全,二者最本质的区别在于:synchronized 是 JVM 内置的关键字级锁,ReentrantLock 是 JDK 提供的 API 级锁(基于 AQS 抽象队列同步器实现)。


一、synchronized 核心特性

1. 使用方式:自动加锁释放,无需手动控制

synchronized 由 JVM 隐式管理加锁和解锁,代码执行完成自动释放锁;即使方法抛出异常,JVM 也会自动释放锁,不会出现锁泄露。

  • 修饰实例方法:锁的是当前实例对象(this)
  • 修饰静态方法:锁的是当前类的 Class 对象
  • 修饰代码块:锁的是括号内指定的对象
2. 底层原理:基于对象头 Mark Word 的锁升级机制

锁的状态记录在对象头的 Mark Word 中,随着竞争程度从低到高,锁会单向膨胀升级(只能升级、不能降级),分为三个阶段:

  • 偏向锁:第一个线程访问时,直接将锁偏向该线程,Mark Word 记录线程 ID;后续该线程再次进入无需任何同步操作,无竞争时性能接近无锁。
  • 轻量级锁:当有第二个线程竞争锁时,偏向锁先撤销,再升级为轻量级锁;线程通过 CAS 自旋方式抢锁,不会阻塞挂起,短时间竞争下性能优异。
  • 重量级锁:自旋多次仍抢不到锁,升级为重量级锁,线程进入内核态阻塞,依赖操作系统互斥量实现,开销较高。
3. 固有特性
  • 天生支持可重入:同一个线程多次加锁,锁计数器递增,释放时递减,归零后真正释放锁
  • 只有非公平模式,不支持公平锁

二、ReentrantLock 核心特性(与 synchronized 对比)

ReentrantLock 是 java.util.concurrent.locks.Lock 接口的核心实现类,基于 AQS 框架实现,最大优势是灵活性高。

1. 使用方式:手动控制加锁解锁

必须手动调用 lock() 加锁、unlock() 解锁,通常配合 try-finally 块使用,确保异常场景下也能释放锁,避免锁泄露。

2. 比 synchronized 多的核心能力

这也是二者最核心的功能差异:

  • 可超时获取锁:调用 tryLock(timeout),指定时间内拿不到锁就直接放弃,避免死等,可有效预防死锁
  • 可响应中断:调用 lockInterruptibly(),等待锁的过程中可以响应中断,退出等待
  • 支持公平 / 非公平双模式:
    • 非公平锁(默认):线程直接抢锁,吞吐量高,但极端情况下可能出现线程饥饿
    • 公平锁:构造方法传入 true 开启,严格按照线程排队顺序分配锁,无饥饿但整体吞吐量更低
  • 绑定多个 Condition 条件队列:可以创建多个 Condition 对象,实现精准的线程唤醒(比如生产 / 消费分别唤醒);而 synchronized 只有一个 wait/notify 等待队列,唤醒时容易造成大量无效唤醒。
3. 固有特性
  • 同样支持可重入,和 synchronized 机制类似,基于 AQS 的 state 计数器实现

三、扩展:读写锁 ReentrantReadWriteLock

synchronized 和 ReentrantLock 都属于独占锁,同一时间只能有一个线程持有。而读写锁是「共享 + 独占」的组合模式,更适合读多写少的场景:

  • 核心规则:读读共享、读写互斥、写写互斥
    • 读锁是共享锁,多个读线程可以同时持有,互不阻塞
    • 写锁是独占锁,和读锁、其他写锁都互斥
  • 典型场景:本地缓存、配置中心等读多写少的场景,相比独占锁能大幅提升并发性能

四、锁选型的核心标准

  1. 普通简单同步场景:优先用 synchronized,语法简单、不易出错,JVM 持续优化,低竞争下性能优异
  2. 需要灵活控制锁的场景:比如超时获取、可中断、公平锁、精准唤醒等,用 ReentrantLock
  3. 读多写少的场景:直接用 ReentrantReadWriteLock
  4. 高并发细粒度场景:优先考虑 CAS 原子类、ConcurrentHashMap 等内置细粒度锁的并发容器,避免手动加锁
posted @ 2026-09-15 11:04  堭鍙銤  阅读(4)  评论(0)    收藏  举报