知识点详细说明
线程同步机制是解决多线程环境下资源竞争和数据不一致问题的核心手段。以下是Java中常用的线程同步方式及其原理、适用场景和注意事项:
1. synchronized
关键字
原理
-
锁机制:基于对象监视器(Monitor),同一时刻只有一个线程能持有锁。
-
锁对象:
- 实例方法:锁对象为当前实例(
this
)。 - 静态方法:锁对象为类的
Class
对象。 - 同步代码块:锁对象由用户指定(如
Object lock
)。
-
注意:线程同步的本质是:线程排队执行就是同步机制。
-
语法格式 :
synchronized(必须是需要排队的这几个线程共享的对象){
//需要同步的代码
}
示例
public synchronized void add() {count++;
}
public static synchronized void staticAdd() {staticCount++;
}
public void blockAdd() {synchronized (lockObject) {count++;}
}
优点与缺点
优点 | 缺点 |
---|
语法简单,自动释放锁。 | 功能单一(不可中断、不支持超时)。 |
无需手动管理锁的获取与释放。 | 锁粒度粗时性能较差。 |
适用场景
2. ReentrantLock
显式锁
原理
- 可重入锁:允许同一线程多次获取同一把锁。
- 高级功能:支持公平锁、可中断锁、超时尝试获取锁、条件变量(
Condition
)。
示例
private final ReentrantLock lock = new ReentrantLock();public void add() {lock.lock();try {count++;} finally {lock.unlock();}
}
优点与缺点
优点 | 缺点 |
---|
支持灵活的锁控制(如超时)。 | 需手动释放锁,易遗漏导致死锁。 |
可配合Condition 实现精准唤醒。 | 代码复杂度较高。 |
适用场景
3. volatile
关键字
原理
- 可见性:强制线程从主内存读取变量,修改后立即写回主内存。
- 禁止指令重排序:通过内存屏障(Memory Barrier)保证代码执行顺序。
示例
private volatile boolean flag = false;public void setFlag() {flag = true;
}
优点与缺点
优点 | 缺点 |
---|
轻量级,无锁竞争。 | 仅保证可见性,不保证原子性。 |
适合单写多读场景。 | 无法解决复合操作(如i++)的线程安全。 |
适用场景
4. 原子类(AtomicInteger
等)
原理
- CAS操作:通过CPU指令(Compare-And-Swap)实现无锁原子操作。
示例
private AtomicInteger count = new AtomicInteger(0);public void add() {count.incrementAndGet();
}
优点与缺点
优点 | 缺点 |
---|
高性能(无锁竞争)。 | 仅适用于单一变量的简单操作。 |
无需显式同步。 | 无法处理复杂逻辑的原子性。 |
适用场景
5. 线程同步工具类
(1) CountDownLatch
(2) CyclicBarrier
(3) Semaphore
6. 线程封闭(ThreadLocal
)
原理
优点与缺点
优点 | 缺点 |
---|
彻底避免线程安全问题。 | 可能引发内存泄漏(需及时清理)。 |
适合线程间数据隔离的场景。 | 不适用于跨线程数据共享。 |
同步机制对比总结
机制 | 锁类型 | 性能 | 适用场景 |
---|
synchronized | 悲观锁 | 中等 | 简单同步块或方法 |
ReentrantLock | 悲观锁 | 中高 | 复杂锁逻辑(超时、条件变量) |
volatile | 无锁(可见性) | 高 | 状态标志位 |
原子类 | 无锁(CAS) | 极高 | 计数器、简单变量更新 |
工具类 | 依赖具体实现 | 可变 | 多线程协同(如计数等待、限流) |
ThreadLocal | 无锁(线程封闭) | 高 | 线程隔离数据(如数据库连接) |
记忆方法
- 口诀:
同步锁,原子类,volatile保可见;工具协同ThreadLocal,各司其职解难题。
- 对比记忆:
synchronized
像公共电话亭(一次一人使用)。ReentrantLock
像智能门锁(可设置密码、超时)。ThreadLocal
像个人储物柜(各自独立,互不干扰)。
最佳实践
- 优先使用无锁方案:如原子类、
ThreadLocal
。 - 减小锁粒度:同步代码块 > 同步方法,锁对象分离(如细粒度锁)。
- 避免锁嵌套:预防死锁,按固定顺序获取锁。
- 及时释放资源:
ReentrantLock
需在finally
中解锁,ThreadLocal
使用后调用remove()
。