synchronized
是 Java 中用于实现线程同步的关键字,它提供了一种简单有效的方式来控制多线程对共享资源的访问,确保线程安全。
基本概念
synchronized
关键字可以实现:
-
互斥性:同一时刻只有一个线程可以执行被同步的代码
-
可见性:线程对共享变量的修改对其他线程立即可见
使用方式
synchronized
有三种主要使用方式:
1. 同步实例方法
java
public synchronized void method() {
// 同步代码
}
-
锁对象是当前实例对象(this)
-
同一实例的同步方法在同一时刻只能被一个线程访问
2. 同步静态方法
java
public static synchronized void staticMethod() {
// 同步代码
}
-
锁对象是当前类的 Class 对象(如 MyClass.class)
-
所有实例的同步静态方法在同一时刻只能被一个线程访问
3. 同步代码块
java
public void method() {
// 非同步代码
synchronized(lockObject) {
// 同步代码
}
// 非同步代码
}
-
可以灵活指定锁对象
-
缩小同步范围,提高并发性能
实现原理
synchronized
是基于 JVM 内置锁(Monitor)实现的:
-
进入同步代码:线程尝试获取对象的 Monitor
-
成功则持有 Monitor,计数器 +1
-
失败则进入阻塞队列等待
-
-
退出同步代码:释放 Monitor,计数器 -1
-
计数器为 0 时完全释放锁
-
唤醒等待队列中的线程
-
锁的升级过程(JDK 1.6+优化)
为了减少锁操作的开销,JVM 实现了锁升级机制:
-
无锁状态:对象刚创建时
-
偏向锁:适用于只有一个线程访问的场景
- 记录线程 ID,减少同步开销
-
轻量级锁(自旋锁):适用于短时间等待的场景
- 线程通过 CAS 自旋尝试获取锁
-
重量级锁:竞争激烈时升级
- 线程进入阻塞状态,由操作系统管理
注意事项
-
锁对象选择:
-
不要使用 String 常量等可能被共享的对象作为锁
-
建议使用专门创建的私有 final 对象作为锁
-
-
死锁风险:
-
避免嵌套获取多个锁
-
如果需要获取多个锁,应保持一致的获取顺序
-
-
性能考虑:
-
尽量减小同步代码块的范围
-
避免在同步代码块中执行耗时操作
-
与 Lock 接口的比较
特性 | synchronized | Lock (如 ReentrantLock) |
---|---|---|
实现方式 | JVM 内置 | Java 代码实现 |
锁获取方式 | 自动获取释放 | 需要手动 lock/unlock |
尝试非阻塞获取锁 | 不支持 | tryLock() 支持 |
公平锁 | 非公平 | 可配置公平/非公平 |
条件变量 | 单一 | 可创建多个 Condition |
性能 | JDK 优化后接近 | 更灵活但稍复杂 |
synchronized
是 Java 中最基本的线程同步机制,虽然功能不如 Lock
接口丰富,但在大多数情况下已经足够,并且随着 JVM 的优化,其性能已经非常优秀。