CATS
138.50M · 2026-02-04
synchronized是JVM层面实现的,无需手动释放锁,属于内置锁。ReentrantLock为代表的显式锁,需要手动释放锁,功能更加灵活,位于java.util.concurrent.locks包,Java代码层面实现。synchronized和ReentrantLock都为悲观锁,每次操作资源都会加锁,阻塞其他线程。AtomicInteger。synchronized和ReentrantLock均为可重入锁。synchronized会根据竞争激烈程度自动升级,逐步提升并发性能)synchronizedpublic class SynchronizedDemo {
// 1. 修饰实例方法:锁的是「当前类的实例对象」(this)
public synchronized void instanceMethod() {
// 线程安全的实例方法逻辑
System.out.println("修饰实例方法,锁:" + this);
}
// 2. 修饰静态方法:锁的是「当前类的 Class 对象」(SynchronizedDemo.class)
public static synchronized void staticMethod() {
// 线程安全的静态方法逻辑
System.out.println("修饰静态方法,锁:" + SynchronizedDemo.class);
}
// 3. 修饰同步代码块:锁的是「括号内指定的对象」(灵活可控)
public void codeBlockMethod() {
// 可选锁对象:this(实例对象)、Class 对象、自定义任意对象
Object lock = new Object();
synchronized (lock) {
// 线程安全的代码块逻辑
System.out.println("修饰代码块,锁:" + lock);
}
}
}
⭐synchronized 竞争的是一个资源对象,无论是修饰的实例方法、静态方法还是代码块,只是对象的类型不同而已。(可以是方法当前类的实例对象、class对象、自定义的任意对象)
依赖于「Java对象头」和「监视器锁」,不同JDK版本略有差异
ReentrantLockimport java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
// 1. 创建 ReentrantLock 实例(默认非公平锁,传入 true 为公平锁)
private static final ReentrantLock lock = new ReentrantLock(false);
public void doTask() {
// 2. 获取锁(lock() 方法,阻塞式获取)
lock.lock();
try {
// 3. 执行线程安全的业务逻辑
System.out.println(Thread.currentThread().getName() + " 已获取锁,执行任务");
} finally {
// 4. 释放锁(必须在 finally 中,保证无论是否异常,都能释放锁)
lock.unlock();
System.out.println(Thread.currentThread().getName() + " 已释放锁");
}
}
public static void main(String[] args) {
ReentrantLockDemo demo = new ReentrantLockDemo();
// 启动 2 个线程竞争锁
new Thread(demo::doTask, "线程1").start();
new Thread(demo::doTask, "线程2").start();
}
}
⭐finally释放锁是必须的,防止try抛出异常,锁无法释放,会导致其他线程永久阻塞,引发死锁。
ReentrantLock提供了synchronized不具备的高级功能:
trylock(long timeout, TimeUnit unit)方法,设置锁获取超时时间,超时后自动放弃获取锁,返回false,避免长时间阻塞。lockInterruptibly()方法,获取锁的线程可被其他线程中断(调用thread.interrupt()),中断后抛出 InterruptedException,并释放锁,灵活控制线程状态。isLocked()(判断锁是否被持有)、isHeldByCurrentThread()(判断当前线程是否持有该锁)、getHoldCount()(获取当前线程持有该锁的次数),方便监控和调试。ReentrantLock 的底层基于 AQS(AbstractQueuedSynchronizer,抽象队列同步器) 实现,AQS 是 JUC 包中所有显式锁、同步工具类的核心框架。
核心逻辑:
state」和一个「双向链表同步队列」。state 用于标记锁的状态:state=0 表示锁未被持有,state>0 表示锁被持有(state 的值等于线程持有锁的次数,体现可重入性)。state:若 state=0,则将 state 改为 1,获取成功;若 state>0 且是当前线程持有,则 state 加 1(可重入),否则加入同步队列阻塞等待。state 减 1,当 state=0 时,释放锁并唤醒同步队列中的下一个线程。synchronized 一样,支持线程重复获取同一把锁,通过 state 变量计数实现。unlock() 释放,依赖开发者规范,灵活性高但风险也高(容易遗漏释放)。synchronized(JDK 1.6 后 synchronized 优化,两者性能差距不大)。synchronized锁升级锁的载体 -- Java对象头:synchronized 锁的是「对象」,而锁状态的信息,就存储在 Java 对象头(Object Header) 中(仅针对普通对象,数组对象的对象头额外包含数组长度)。
Java 对象头在 32 位 JVM 和 64 位 JVM 中长度分别为 8 字节和 16 字节,核心包含两部分(以 64 位 JVM 为例):
| 锁状态 | Mark Word 核心字段(64 位) |
|---|---|
| 无锁 | 哈希码(25 位)+ GC 年龄(4 位)+ 无锁标记(1 位) |
| 偏向锁 | 偏向线程 ID(54 位)+ 偏向时间戳(2 位)+ GC 年龄(4 位)+ 偏向锁标记(1 位) |
| 轻量级锁 | 指向栈中锁记录的指针(63 位)+ 轻量级锁标记(1 位) |
| 重量级锁 | 指向监视器锁(Monitor)的指针(63 位)+ 重量级锁标记(1 位) |
Mark Word 中的「偏向线程 ID」设置为当前线程的 ID,同时将「锁标记位」改为「偏向锁标记」。Mark Word 中的:
Mark Word 的副本(称为「Displaced Mark Word」)。Mark Word 中的内容替换为「指向当前线程栈中锁记录的指针」。Mark Word 的锁标记位改为「轻量级锁标记」,线程继续执行同步代码。Mark Word 中的「锁记录指针」替换回原来的「Displaced Mark Word」(栈中锁记录存储的副本)。Mark Word 中会存储「指向 Monitor 的指针」,Monitor 是一个用于管理锁竞争的核心数据结构。owner 字段记录持有锁的线程)。owner 字段设置为当前线程,线程执行同步代码。owner 字段置为 null)。