一句话总结
互斥锁(Mutex)通过独占访问保证资源安全;信号量(Semaphore)控制线程并发数量;条件变量(Condition Variable)实现线程间状态通知;读写锁(Read-Write Lock)区分读写操作提升效率;原子操作(Atomic)通过硬件指令确保操作的不可分割性。
详细解析
Java线程同步主要通过以下几种机制实现,确保多线程环境下共享资源的安全访问:
1.synchronized关键字
- 作用:提供互斥锁,确保同一时间只有一个线程访问同步代码块或方法。
- 使用方式:
同步实例方法:锁对象为当前实例。
public synchronized void method() {
// 同步代码
}
同步静态方法:锁对象为类的Class对象。
public static synchronized void staticMethod() {
// 同步代码
}
同步代码块:显式指定锁对象(任意对象)。
public void method() {
synchronized (lockObject) {
// 同步代码
}
}
- 特点:
隐式加锁/解锁,代码简洁。JDK 1.6 后优化了锁机制(偏向锁、轻量级锁、重量级锁),性能提升显著。不可中断,无法设置超时。
2.Lock接口(如ReentrantLock)
- 作用:提供更灵活的显式锁操作。
- 使用方式:
Lock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 同步代码
} finally {
lock.unlock(); // 确保解锁
}
}
- 特点:支持可中断锁(lockInterruptibly())、超时锁(tryLock(timeout))、公平锁。需手动加锁/解锁,避免死锁。适合高竞争或复杂同步场景。
3.volatile关键字
- 作用:保证变量的可见性,禁止指令重排序。
- 使用方式:
private volatile boolean flag = false;
- 适用场景:状态标记(如线程终止标志)。单例模式的双重检查锁定(DCL)。
- 限制:不保证原子性,无法替代锁。
4.wait()与notify()/notifyAll()
- 作用:实现线程间协调(生产者-消费者模型)。
- 使用方式(需配合synchronized):
// 消费者线程
synchronized (lock) {
while (condition) {
lock.wait(); // 释放锁并等待
}
// 处理逻辑
}
// 生产者线程
synchronized (lock) {
// 修改条件
lock.notify(); // 唤醒一个等待线程
// 或 lock.notifyAll();
}
- 特点:
- wait()释放锁,notify()唤醒等待线程。
- 需在循环中检查条件(避免虚假唤醒)。
5. 原子类(如AtomicInteger)
- 作用:通过 CAS(Compare and Swap)实现无锁原子操作。
- 使用方式:
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // 原子递增
- 适用场景:计数器、状态标记等简单原子操作。性能优于锁,但仅支持单一变量。
6. 线程安全容器(如ConcurrentHashMap)
- 作用:内置同步机制,避免手动同步。
- 常见实现:ConcurrentHashMap:分段锁或 CAS 实现高并发访问。CopyOnWriteArrayList:写时复制,适合读多写少场景。
- 特点:简化代码,直接使用即可保证线程安全。
对比与选型建议
机制 | 适用场景 | 优点 | 缺点 |
synchronized | 简单同步需求,低竞争场景 | 代码简洁,JVM优化好 | 功能有限,不可中断 |
Lock | 高竞争场景,需复杂控制(超时、公平) | 灵活,支持多种锁策略 | 需手动管理锁,代码复杂 |
volatile | 状态标记,可见性需求 | 轻量级,无锁性能高 | 不保证原子性 |
wait/notify | 线程间协作(生产者-消费者) | 内置线程协调机制 | 需配合锁使用,易出错 |
原子类 | 简单原子操作(如计数器) | 无锁高性能 | 仅支持单一变量 |
线程安全容器 | 替代手动同步的集合操作 | 简化代码,高效安全 | 功能可能受限 |