程同步与锁
多线程访问共享资源需要同步机制保证线程安全,synchronized 是最基础的方式。
程安全问题
Java
// 不安全的计数器
public class UnsafeCounter {
private int count = 0;
public void increment() {
count++; // 读-改-写三步,非原子
}
public int getCount() {
return count;
}
}
// 多线程并发调用 increment(),结果不准确
count++ 不是原子操作:读取 count、加 1、写入 count,多线程交错执行导致数据丢失。
synchronized 解决线程安全
同步方法
Java
public class SafeCounter {
private int count = 0;
// 同步实例方法:锁当前对象(this)
public synchronized void increment() {
count++;
}
// 同步静态方法:锁 Class 对象
public static synchronized void staticMethod() {
// 同步代码
}
}
同步代码块
Java
public class SafeCounter {
private int count = 0;
private final Object lock = new Object(); // 专用锁对象
public void increment() {
synchronized (lock) { // 锁指定对象
count++;
}
}
public void method() {
synchronized (this) { // 锁当前对象
// 同步代码
}
synchronized (SafeCounter.class) { // 锁 Class 对象
// 同步代码
}
}
}
推荐专用锁对象:private final Object lock = new Object(),避免外部干扰。
synchronized 锁对象
| 用法 | 锁对象 | 适用场景 |
|---|---|---|
| synchronized 方法 | this | 实例级同步 |
| synchronized 静态方法 | Class 对象 | 类级同步 |
| synchronized(this) | 当前实例 | 代码块同步 |
| synchronized(obj) | 指定对象 | 精细控制 |
| synchronized(Class.class) | Class 对象 | 类级同步 |
锁升级机制
JVM 对 synchronized 优化,锁根据竞争情况升级:
Java
无锁 → 偏向锁 → 轕量级锁 → 重量级锁
| 锁状态 | 条件 | 特点 |
|---|---|---|
| 偏向锁 | 单线程重复获取 | 无同步开销,最快 |
| 蕃量级锁 | 多线程交替执行 | CAS 自旋,有少量开销 |
| 重量级锁 | 竞争激烈 | OS 互斥量,开销最大 |
锁升级不可逆:升级后不会降级,竞争激烈时锁一直为重量级。
synchronized 注意事项
1. 避免锁可变对象
Java
// 错误:锁对象可能被修改
private Object lock = new Object();
lock = new Object(); // 锁变了,失效
// 正确:final 确保锁不变
private final Object lock = new Object();
2. 避免嵌套锁
Java
// 错误:可能死锁
synchronized (lockA) {
synchronized (lockB) {
// 代码
}
}
3. 尽量减小锁范围
Java
// 不推荐:锁住整个方法
public synchronized void process() {
// 大量非同步代码
synchronized (this) {
count++; // 只有这行需要同步
}
}
// 推荐:只锁必要部分
public void process() {
// 大量非同步代码(更快)
synchronized (this) {
count++;
}
}
4. 避免锁住方法参数
text
// 错误:外部对象可能被其他代码锁住
public void method(Object param) {
synchronized (param) {
// 可能与其他代码冲突
}
}
synchronized vs ReentrantLock
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 获取释放 | 自动 | 手动 unlock |
| 公平锁 | ❌ 非公平 | ✅ 可选公平 |
| 可中断 | ❌ | ✅ lockInterruptibly |
| 超时获取 | ❌ | ✅ tryLock(timeout) |
| 条件变量 | 单个 | 多个 Condition |
| 性能 | JDK 6 后差距小 | 略有优势 |
选择原则:优先使用 synchronized,需要公平锁、可中断、超时等高级功能时用 ReentrantLock。
要点总结
- synchronized 保证线程安全,自动获取释放锁
- 同步方法锁 this(实例)或 Class(静态)
- 同步代码块可指定任意对象作为锁
- 推荐 private final Object 作为专用锁
- 锁升级:偏向锁 → 蕃量级锁 → 重量级锁
- 避免锁可变对象、嵌套锁、大范围锁
- JDK 6 后 synchronized 性能已优化,与 ReentrantLock 差距小
📝 发现内容有误?点击此处直接编辑