## 引言
在多线程编程中,线程安全是开发者面临的核心挑战之一。当多个线程并发访问共享资源时,如果没有正确的同步机制,就可能出现数据不一致、竞态条件等问题。Java提供了多种线程锁机制来保障线程安全,本文将深入探讨Java中的各种锁实现、使用场景和优化策略。
一、线程锁的核心作用
1.1 互斥访问
确保同一时刻只有一个线程可以访问临界区代码,防止并发修改导致的数据不一致。
1.2 可见性保证
通过锁机制强制内存屏障(Memory Barrier),保证变量的修改对所有线程可见。
1.3 有序性
禁止指令重排序优化(Happens-Before原则),确保代码执行顺序符合预期。
二、内置锁:synchronized关键字
2.1 基本用法
//实例方法锁
public synchronized void methodA() { ... }
// 静态方法锁
public static synchronized void methodB() { ... }
// 代码块锁
public void methodC() {
synchronized(this) { ... }
}
2.2 核心特性
- **可重入性**:线程可以重复获取已持有的锁
- **非公平锁**:不保证等待时间最长的线程优先获取锁
- **自动释放**:通过JVM隐式管理锁的获取与释放
2.3 优缺点分析
**优势**:
- 语法简洁,无需显式释放
- JVM内置优化(锁消除、锁粗化等)
**局限性**:
- 无法中断等待线程
- 不能设置超时时间
- 只能以块结构方式使用
三、显式锁:ReentrantLock
3.1 基本使用
private final ReentrantLock lock = new ReentrantLock();
public void performAction() {
lock.lock();
try {
// 临界区代码
} finally {
lock.unlock();
}
}
### 3.2 高级特性
- **可中断获取**:`lockInterruptibly()`
- **超时机制**:`tryLock(long timeout, TimeUnit unit)`
- **公平性选择**:构造方法指定公平策略
- **条件变量**:`newCondition()`实现精准唤醒
四、读写锁:ReentrantReadWriteLock
4.1 读写分离原理
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
public Object read() {
readLock.lock();
try { /* 读操作 */ }
finally { readLock.unlock(); }
}
public void write(Object data) {
writeLock.lock();
try { /* 写操作 */ }
finally { writeLock.unlock(); }
}
五、锁优化技术
5.1 减少锁粒度
将大对象锁拆分为多个细粒度锁:
// 原始方式
synchronized(monitor) { /* 所有操作 */ }
// 优化后
synchronized(segment1) { /* 部分操作 */ }
synchronized(segment2) { /* 其他操作 */ }
5.2 锁分离技术
使用`LinkedBlockingQueue`的put锁和take锁分离实现。
5.3 无锁编程替代
- 原子类:AtomicInteger等
- LongAdder:高并发计数场景
- ConcurrentHashMap:分段锁实现
5.4 锁粗化优化
// 不推荐
for(int i=0; i<100; i++) {
synchronized(lock) { /* 小操作 */ }
}
// 推荐
synchronized(lock) {
for(int i=0; i<100; i++) { /* 批量操作 */ }
}
六、锁选择策略
6.1 选择依据
1. 并发竞争强度
2. 读写比例
3. 功能需求(公平性、超时等)
4. 性能要求
5. 代码可维护性
6.2 推荐方案
- 简单同步 → `synchronized`
- 复杂需求 → `ReentrantLock`
- 读多写少 → `ReadWriteLock`
- 极高并发 → `StampedLock`