1. 锁的基本概念:从现实世界到代码世界
1.1 锁的演进:synchronized → Lock
想象一下健身房储物柜的使用场景:
- synchronized :像固定密码锁 - 简单易用但功能有限
- Lock接口 :像智能电子锁 - 功能丰富且灵活可控
java
// synchronized - 固定密码锁
public synchronized void oldMethod() {
// 自动上锁和解锁
// 但无法中断、无法超时、无法尝试获取
}
// Lock - 智能电子锁
public void newMethod() {
Lock lock = new ReentrantLock();
lock.lock(); // 手动开锁
try {
// 临界区代码
} finally {
lock.unlock(); // 手动关锁
}
}
1.2 Lock接口的核心优势
| 特性 | synchronized | Lock |
|---|---|---|
| 中断响应 | ❌ | ✅ |
| 超时控制 | ❌ | ✅ |
| 尝试获取 | ❌ | ✅ |
| 公平性 | ❌ | ✅ |
| 条件队列 | 单个 | 多个 |
2. AQS:并发世界的交通指挥中心
2.1 AQS的核心设计思想
AQS(AbstractQueuedSynchronizer)就像高速公路收费站系统:
- state状态:当前可通行的车道数量
- 同步队列:等待通行的车辆排队
- CAS操作:智能的车辆调度系统
java
/**
* AQS同步状态管理示例
*/
public class AQSCoreConcept {
// state字段的三种典型用法:
// 1. 互斥锁:state = 0(未锁定) 或 1(已锁定)
// 2. 重入锁:state = 重入次数
// 3. 读写锁:高16位 = 读锁计数,低16位 = 写锁计数
private volatile int state;
// 三个核心的state操作方法:
protected final int getState() { return state; }
protected final void setState(int newState) { state = newState; }
protected final boolean compareAndSetState(int expect, int update) {
// CAS原子操作,保证线程安全
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
}
2.2 同步队列:线程的"等候区"
java
/**
* AQS同步队列结构演示
*/
public class SyncQueueDemo {
/**
* 同步队列节点结构(双向链表):
*
* head (虚拟节点) ↔ [prev|thread|next|waitStatus] ↔ [prev|thread|next|waitStatus] ↔ tail
*
* waitStatus状态说明:
* - CANCELLED(1):线程已取消
* - SIGNAL(-1):后继线程需要被唤醒
* - CONDITION(-2):线程在Condition队列中
* - PROPAGATE(-3):共享模式下传播唤醒
*/
// 独占模式获取锁的典型流程
public void acquireDemo() {
Lock lock = new ReentrantLock();
// 底层调用AQS的acquire方法
lock.lock(); // -> sync.acquire(1);
/**
* acquire方法执行流程:
* 1. tryAcquire()尝试直接获取锁
* 2. 失败 → addWaiter()加入同步队列队尾
* 3. acquireQueued()在队列中自旋等待
* 4. 被前驱节点唤醒后重新尝试获取锁
*/
}
}
2.3 自定义锁实战:基于AQS实现TwinsLock
java
/**
* TwinsLock - 同一时刻最多允许两个线程访问的共享锁
* 设计思路:将AQS的state作为许可证计数器
*/
public class TwinsLock implements Lock {
private final Sync sync = new Sync(2);
private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) {
if (count <= 0) throw new IllegalArgumentException("计数必须大于0");
setState(count); // 初始化许可证数量
}
/**
* 共享模式获取锁
* @return 负数:获取失败;0:获取成功但无剩余;正数:获取成功且有剩余
*/
@Override
public int tryAcquireShared(int reduceCount) {
for (;;) { // 自旋避免CAS失败
int current = getState();
int newCount = current - reduceCount;
// 如果新计数<0(无许可证)或CAS设置成功,返回结果
if (newCount < 0 || compareAndSetState(current, newCount)) {
return newCount;
}
}
}
/**
* 共享模式释放锁
*/
@Override
public boolean tryReleaseShared(int returnCount) {
for (;;) {
int current = getState();
int newCount = current + returnCount;
if (compareAndSetState(current, newCount)) {
return true;
}
}
}
}
@Override
public void lock() {
sync.acquireShared(1); // 获取1个许可证
}
@Override
public void unlock() {
sync.releaseShared(1); // 释放1个许可证
}
// 其他Lock方法实现...
}
/**
* 测试TwinsLock
*/
public class TwinsLockTest {
@Test
public void testTwinsLock() {
final Lock lock = new TwinsLock();
// 启动10个线程,但同一时刻只有2个能获取锁
for (int i = 0; i < 10; i++) {
Thread worker = new Thread(() -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获取锁");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
});
worker.start();
}
}
}
3. 重入锁ReentrantLock:可重复使用的智能锁
3.1 重入性:一把钥匙开多把锁
现实比喻:你进了自家大门,还可以用同一把钥匙打开卧室门、书房门。
java
/**
* 重入锁的重入特性演示
*/
public class ReentrantDemo {
private final ReentrantLock lock = new ReentrantLock();
public void outer() {
lock.lock();
try {
System.out.println("外层方法获取锁,重入计数: " + getHoldCount());
inner(); // 重入:同一个线程再次获取同一把锁
} finally {
lock.unlock();
}
}
public void inner() {
lock.lock(); // 这里不会阻塞,因为已经是锁的持有者
try {
System.out.println("内层方法获取锁,重入计数: " + getHoldCount());
} finally {
lock.unlock();
}
}
private int getHoldCount() {
// 返回当前线程持有该锁的次数
return lock.getHoldCount();
}
}
3.2 公平锁 vs 非公平锁
公平锁 :像银行取号排队 - 先来先服务
非公平锁 :像公交车抢座位 - 谁能抢到谁坐
java
/**
* 公平性对比测试
*/
public class FairVsUnfairTest {
private static Lock fairLock = new ReentrantLock(true); // 公平锁
private static Lock unfairLock = new ReentrantLock(false); // 非公平锁
@Test
public void comparePerformance() {
// 测试结果通常显示:
// - 公平锁:保证顺序,但性能较低
// - 非公平锁:可能饥饿,但吞吐量高
testLock("公平锁", fairLock);
testLock("非公平锁", unfairLock);
}
private void testLock(String type, Lock lock) {
long start = System.currentTimeMillis();
// 多个线程竞争锁...
long duration = System.currentTimeMillis() - start;
System.out.println(type + " 耗时: " + duration + "ms");
}
}
3.3 重入锁实现原理
java
/**
* 重入锁核心实现解析
*/
public class ReentrantLockCore {
/**
* 非公平锁获取逻辑
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { // 锁空闲
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) { // 重入
int nextc = c + acquires;
if (nextc < 0) throw new Error("超过最大锁计数");
setState(nextc); // 增加重入计数
return true;
}
return false;
}
/**
* 释放锁逻辑
*/
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { // 完全释放
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
}
4. 读写锁ReentrantReadWriteLock:读写分离的高并发锁
4.1 读写锁的应用场景
现实比喻:图书馆管理规则
- 读操作:多人可同时阅读同一本书
- 写操作:修改书籍时需独占访问
java
/**
* 基于读写锁的缓存实现
*/
public class ReadWriteCache<K, V> {
private final Map<K, V> cache = new HashMap<>();
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
/**
* 读操作:共享锁,允许多个线程同时读
*/
public V get(K key) {
readLock.lock();
try {
String value = cache.get(key);
// 模拟配置读取的耗时操作
simulateProcess(1);
return value;
} finally {
readLock.unlock();
}
}
/**
* 批量获取配置 - 读锁支持并发
*/
public Map<String, String> getConfigs(Set<String> keys) {
readLock.lock();
try {
Map<String, String> result = new HashMap<>();
for (String key : keys) {
result.put(key, cache.get(key));
}
simulateProcess(keys.size());
return result;
} finally {
readLock.unlock();
}
}
/**
* 更新配置 - 低频操作,使用写锁
*/
public void updateConfig(String key, String value) {
writeLock.lock();
try {
// 模拟配置更新的耗时操作
simulateProcess(10);
cache.put(key, value);
System.out.println("配置更新: " + key + " = " + value);
} finally {
writeLock.unlock();
}
}
private void simulateProcess(int milliseconds) {
try {
Thread.sleep(milliseconds);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
4.2 读写锁的状态设计
java
/**
* 读写锁状态设计的精妙之处
*/
public class ReadWriteStateDesign {
/**
* 32位state字段的划分:
*
* ┌─────────────────┬─────────────────┐
* │ 高16位 │ 低16位 │
* │ 读状态 │ 写状态 │
* │ (读锁计数) │ (写锁重入数) │
* └─────────────────┴─────────────────┘
*/
// 获取写状态(低16位)
static int exclusiveCount(int c) {
return c & 0x0000FFFF;
}
// 获取读状态(高16位)
static int sharedCount(int c) {
return c >>> 16;
}
// 读锁计数+1
int newReadState = currentState + (1 << 16); // 即 + 0x00010000
// 写锁计数+1
int newWriteState = currentState + 1;
}
4.3 锁降级:保证数据可见性的重要技术
java
/**
* 锁降级示例:写锁 → 读锁
* 目的:保证数据的可见性
*/
public class LockDemotionExample {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
private volatile boolean update = false;
private Object data;
public void processData() {
readLock.lock();
if (!update) {
// 数据需要更新,必须先释放读锁
readLock.unlock();
// 获取写锁
writeLock.lock();
try {
// 双重检查
if (!update) {
// 准备数据...
data = fetchData();
update = true;
}
// 关键步骤:在释放写锁前获取读锁
readLock.lock(); // 锁降级开始
} finally {
writeLock.unlock(); // 锁降级完成,现在持有读锁
}
}
try {
// 使用数据(仍在读锁保护下)
useData(data);
} finally {
readLock.unlock();
}
}
// 不支持锁升级!可能产生死锁
public void invalidLockUpgrade() {
readLock.lock();
try {
// 危险操作:尝试在持有读锁时获取写锁
// 如果其他线程也持有读锁,会产生死锁
writeLock.lock(); // 可能永远阻塞!
try {
// 修改数据...
} finally {
writeLock.unlock();
}
} finally {
readLock.unlock();
}
}
private Object fetchData() { return null; }
private void useData(Object data) {}
}
5. LockSupport:线程的精准遥控器
5.1 LockSupport的核心能力
LockSupport提供线程阻塞和唤醒的原子操作,就像线程的远程控制器:
java
/**
* LockSupport基础使用
*/
public class LockSupportBasic {
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(() -> {
System.out.println("工作线程开始执行");
System.out.println("工作线程即将被阻塞");
// 阻塞当前线程(停车)
LockSupport.park();
System.out.println("工作线程被唤醒,继续执行");
});
worker.start();
Thread.sleep(2000); // 主线程等待2秒
System.out.println("主线程准备唤醒工作线程");
// 唤醒指定线程(开车)
LockSupport.unpark(worker);
System.out.println("主线程已发送唤醒信号");
}
}
5.2 许可机制:先发后至的灵活性
java
/**
* LockSupport的许可机制演示
* 每个线程有一个许可(最多为1):
* - unpark:添加一个许可
* - park:消耗一个许可,没有许可就阻塞
*/
public class PermitMechanism {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("子线程开始");
try {
Thread.sleep(1000); // 确保主线程先调用unpark
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("子线程调用park");
// 这里不会阻塞,因为主线程已经给了许可
LockSupport.park();
System.out.println("子线程第一次park完成");
// 这次会阻塞,因为许可已经被消耗
LockSupport.park();
System.out.println("子线程第二次park完成");
});
thread.start();
// 立即给子线程许可(先发)
System.out.println("主线程调用unpark");
LockSupport.unpark(thread);
// 等待后再次唤醒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("主线程再次调用unpark");
LockSupport.unpark(thread);
}
}
5.3 Blocker:线程诊断的"身份证"
java
/**
* Blocker的作用:在线程dump中标识等待目标
*/
public class BlockerDemo {
public static void main(String[] args) throws InterruptedException {
Object blocker = new Object(); // 阻塞对象
Thread withBlocker = new Thread(() -> {
System.out.println("带Blocker的线程开始阻塞");
// 推荐:使用带blocker的park方法
LockSupport.park(blocker);
System.out.println("带Blocker的线程被唤醒");
}, "WithBlocker-Thread");
Thread withoutBlocker = new Thread(() -> {
System.out.println("无Blocker的线程开始阻塞");
// 不推荐:无blocker的park方法
LockSupport.park();
System.out.println("无Blocker的线程被唤醒");
}, "WithoutBlocker-Thread");
withBlocker.start();
withoutBlocker.start();
Thread.sleep(1000);
// 在实际环境中使用jstack查看线程dump,区别明显:
// 有Blocker: "parking to wait for <0x00000000d5e8a6c0> (a java.lang.Object)"
// 无Blocker: 只显示在LockSupport.park处等待
LockSupport.unpark(withBlocker);
LockSupport.unpark(withoutBlocker);
withBlocker.join();
withoutBlocker.join();
}
}
6. Condition接口:精准的线程协调器
6.1 Condition vs Object监视器
| 特性 | Object.wait/notify | Condition.await/signal |
|---|---|---|
| 前置条件 | 必须在synchronized内 | 必须先获取Lock |
| 等待队列 | 一个对象一个队列 | 一个Lock多个Condition |
| 精确通知 | 只能notifyAll或随机 | 可以精确通知特定Condition |
| 超时控制 | 有限支持 | 丰富的时间控制方法 |
6.2 Condition实战:有界阻塞队列
java
/**
* 有界阻塞队列 - Condition的经典应用
* 功能:
* - 队列空时,消费者等待
* - 队列满时,生产者等待
*/
public class BoundedBlockingQueue<T> {
private final Object[] items;
private int addIndex, removeIndex, count;
private final Lock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition(); // 非空条件
private final Condition notFull = lock.newCondition(); // 非满条件
public BoundedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
items = new Object[capacity];
}
/**
* 生产方法:队列满时阻塞
*/
public void put(T item) throws InterruptedException {
lock.lock();
try {
// 使用while防止虚假唤醒
while (count == items.length) {
System.out.println("队列已满,生产者等待...");
notFull.await(); // 在notFull条件上等待
}
// 生产元素
items[addIndex] = item;
if (++addIndex == items.length) addIndex = 0;
count++;
System.out.println("生产: " + item + ", 当前数量: " + count);
// 通知可能等待的消费者
notEmpty.signal();
} finally {
lock.unlock();
}
}
/**
* 消费方法:队列空时阻塞
*/
@SuppressWarnings("unchecked")
public T take() throws InterruptedException {
lock.lock();
try {
// 使用while防止虚假唤醒
while (count == 0) {
System.out.println("队列为空,消费者等待...");
notEmpty.await(); // 在notEmpty条件上等待
}
// 消费元素
T item = (T) items[removeIndex];
if (++removeIndex == items.length) removeIndex = 0;
count--;
System.out.println("消费: " + item + ", 当前数量: " + count);
// 通知可能等待的生产者
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}
6.3 Condition内部机制:等待队列与同步队列的协作
java
/**
* Condition内部工作机制解析
*/
public class ConditionInternalMechanism {
/**
* Condition内部有两个重要队列:
*
* 1. 等待队列(Condition队列):单向链表
* firstWaiter → [thread|nextWaiter] → [thread|nextWaiter] → lastWaiter
*
* 2. 同步队列(AQS队列):双向链表
* head ↔ [prev|thread|next] ↔ [prev|thread|next] ↔ tail
*
* await过程:同步队列 → 等待队列
* signal过程:等待队列 → 同步队列
*/
/**
* await方法核心流程:
*/
public final void await() throws InterruptedException {
// 1. 创建节点加入Condition等待队列
Node node = addConditionWaiter();
// 2. 完全释放锁(保存重入状态)
int savedState = fullyRelease(node);
// 3. 阻塞直到被signal或中断
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if (checkInterruptWhileWaiting(node) != 0) break;
}
// 4. 被唤醒后重新竞争锁
if (acquireQueued(node, savedState)) {
// 处理中断...
}
}
/**
* signal方法核心流程:
*/
public final void signal() {
// 1. 必须持有锁才能调用
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 2. 转移等待队列的第一个节点到同步队列
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
}
7. AQS同步队列 vs Condition等待队列
7.1 核心区别总结
| 特性 | AQS同步队列 | Condition等待队列 |
|---|---|---|
| 设计目的 | 管理锁竞争的线程 | 管理条件等待的线程 |
| 数据结构 | 双向链表 | 单向链表 |
| 节点状态 | SIGNAL, CANCELLED, 0, PROPAGATE | CONDITION(-2) |
| 队列数量 | 每个AQS实例1个 | 每个Condition1个 |
| 线程行为 | 竞争锁资源 | 等待特定条件满足 |
| 唤醒方式 | 前驱节点释放锁时唤醒 | 其他线程调用signal()唤醒 |
7.2 队列协作的完整示例
java
/**
* 演示两个队列如何协作工作
*/
public class TwoQueuesCollaboration {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void demonstrate() throws InterruptedException {
Thread waiter = new Thread(() -> {
lock.lock();
try {
System.out.println("Waiter: 获取锁,准备await");
// 此时:
// - Waiter在同步队列中(持有锁)
// - 等待队列为空
condition.await(); // 关键操作!
// await内部完成:
// 1. Waiter节点从同步队列移到等待队列
// 2. 释放锁
// 3. 阻塞等待
System.out.println("Waiter: 被唤醒,重新获得锁");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
});
Thread signaler = new Thread(() -> {
lock.lock();
try {
System.out.println("Signaler: 获取锁,调用signal");
// 此时:
// - Signaler在同步队列中(持有锁)
// - Waiter在等待队列中
condition.signal(); // 关键操作!
// signal内部完成:
// 1. Waiter节点从等待队列移到同步队列
// 2. 唤醒Waiter线程
System.out.println("Signaler: signal完成");
} finally {
lock.unlock(); // 释放锁,Waiter有机会竞争锁
}
});
waiter.start();
Thread.sleep(100); // 确保waiter先执行
signaler.start();
waiter.join();
signaler.join();
}
}
8. 实战指南:如何正确使用Java并发锁
8.1 锁使用的核心原则
原则1:永远在finally块中释放锁
java
/**
* 正确的锁释放方式
*/
public class CorrectLockUsage {
private final Lock lock = new ReentrantLock();
// ✅ 正确做法
public void correctMethod() {
lock.lock();
try {
// 业务逻辑
doBusiness();
} finally {
lock.unlock(); // 确保锁被释放
}
}
// ❌ 错误做法
public void wrongMethod() {
lock.lock();
doBusiness();
lock.unlock(); // 如果doBusiness抛出异常,锁不会被释放!
}
private void doBusiness() {
// 可能抛出异常的业务逻辑
if (Math.random() > 0.5) {
throw new RuntimeException("业务异常");
}
}
}
原则2:避免锁嵌套,预防死锁
java
/**
* 死锁预防示例
*/
public class DeadlockPrevention {
private final Lock lockA = new ReentrantLock();
private final Lock lockB = new ReentrantLock();
// ❌ 容易导致死锁的做法
public void potentialDeadlock() {
lockA.lock();
try {
// 一些操作...
lockB.lock(); // 如果另一个线程以相反顺序获取锁,可能死锁
try {
// 更多操作...
} finally {
lockB.unlock();
}
} finally {
lockA.unlock();
}
}
// ✅ 使用tryLock避免死锁
public void safeMethod() {
while (true) {
if (lockA.tryLock()) {
try {
if (lockB.tryLock()) {
try {
// 成功获取两个锁,执行业务
doBusiness();
return; // 成功完成,退出循环
} finally {
lockB.unlock();
}
}
} finally {
lockA.unlock();
}
}
// 获取锁失败,短暂休息后重试
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
// ✅ 使用固定的锁获取顺序
public void fixedOrderMethod(Object resource1, Object resource2) {
// 通过hash值确定固定的获取顺序
int hash1 = System.identityHashCode(resource1);
int hash2 = System.identityHashCode(resource2);
if (hash1 < hash2) {
lockA.lock();
lockB.lock();
} else {
lockB.lock();
lockA.lock();
}
try {
doBusiness();
} finally {
lockA.unlock();
lockB.unlock();
}
}
private void doBusiness() {}
}
8.2 各组件最佳实践案例
8.2.1 ReentrantLock最佳实践:连接池管理
java
/**
* 基于ReentrantLock的简单数据库连接池
* 特性:支持超时获取、中断响应、连接验证
*/
public class DatabaseConnectionPool {
private final LinkedList<Connection> pool = new LinkedList<>();
private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
private final Condition notEmpty = lock.newCondition();
private final int maxSize;
private final long maxWaitTime;
public DatabaseConnectionPool(int maxSize, long maxWaitMillis) {
this.maxSize = maxSize;
this.maxWaitTime = maxWaitMillis;
initializePool();
}
/**
* 获取连接 - 支持超时和中断
*/
public Connection getConnection() throws InterruptedException, TimeoutException {
lock.lock();
try {
long endTime = System.currentTimeMillis() + maxWaitTime;
while (pool.isEmpty()) {
long remainingTime = endTime - System.currentTimeMillis();
if (remainingTime <= 0) {
throw new TimeoutException("获取连接超时");
}
// 等待连接可用,支持超时
if (!notEmpty.await(remainingTime, TimeUnit.MILLISECONDS)) {
throw new TimeoutException("获取连接超时");
}
}
// 获取并验证连接
Connection conn = pool.removeFirst();
if (!isConnectionValid(conn)) {
conn = createNewConnection();
}
return conn;
} finally {
lock.unlock();
}
}
/**
* 归还连接
*/
public void returnConnection(Connection conn) {
if (conn == null) return;
lock.lock();
try {
if (pool.size() < maxSize && isConnectionValid(conn)) {
pool.addLast(conn);
notEmpty.signal(); // 通知等待的线程
} else {
// 连接池已满或连接无效,关闭连接
closeConnection(conn);
}
} finally {
lock.unlock();
}
}
/**
* 尝试获取连接(非阻塞)
*/
public Connection tryGetConnection() {
if (lock.tryLock()) {
try {
if (!pool.isEmpty()) {
Connection conn = pool.removeFirst();
if (isConnectionValid(conn)) {
return conn;
}
}
} finally {
lock.unlock();
}
}
return null;
}
private boolean isConnectionValid(Connection conn) {
// 连接有效性检查逻辑
try {
return conn != null && !conn.isClosed() && conn.isValid(2);
} catch (SQLException e) {
return false;
}
}
private Connection createNewConnection() {
// 创建新连接的逻辑
return null; // 简化实现
}
private void closeConnection(Connection conn) {
// 关闭连接的逻辑
}
private void initializePool() {
// 初始化连接池
for (int i = 0; i < maxSize / 2; i++) {
pool.add(createNewConnection());
}
}
}
8.2.2 读写锁最佳实践:配置中心
java
/**
* 基于读写锁的配置中心
* 特性:高频读取,低频更新,保证数据一致性
*/
public class ConfigurationCenter {
private final Map<String, String> configs = new HashMap<>();
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
/**
* 获取配置 - 高频操作,使用读锁
*/
public String getConfig(String key) {
readLock.lock();
try {
String value = configs.get(key);
// 模拟配置读取的耗时操作
simulateProcess(1);
return value;
} finally {
readLock.unlock();
}
}
/**
* 批量获取配置 - 读锁支持并发
*/
public Map<String, String> getConfigs(Set<String> keys) {
readLock.lock();
try {
Map<String, String> result = new HashMap<>();
for (String key : keys) {
result.put(key, configs.get(key));
}
simulateProcess(keys.size());
return result;
} finally {
readLock.unlock();
}
}
/**
* 更新配置 - 低频操作,使用写锁
*/
public void updateConfig(String key, String value) {
writeLock.lock();
try {
// 模拟配置更新的耗时操作
simulateProcess(10);
configs.put(key, value);
System.out.println("配置更新: " + key + " = " + value);
} finally {
writeLock.unlock();
}
}
/**
* 批量更新配置 - 写锁保证原子性
*/
public void updateConfigs(Map<String, String> newConfigs) {
writeLock.lock();
try {
simulateProcess(newConfigs.size() * 5);
configs.putAll(newConfigs);
System.out.println("批量更新配置: " + newConfigs.size() + " 项");
} finally {
writeLock.unlock();
}
}
/**
* 热更新配置 - 使用锁降级保证数据一致性
*/
public void hotUpdateConfig(String key, String value) {
// 先获取写锁进行更新
writeLock.lock();
try {
// 更新配置
configs.put(key, value);
System.out.println("热更新配置: " + key + " = " + value);
// 锁降级:在释放写锁前获取读锁
readLock.lock();
} finally {
writeLock.unlock(); // 锁降级完成
}
try {
// 在读锁保护下进行后续操作(如通知观察者、记录日志等)
notifyConfigChange(key, value);
logConfigChange(key, value);
} finally {
readLock.unlock();
}
}
private void notifyConfigChange(String key, String value) {
// 通知配置变更观察者
simulateProcess(2);
}
private void logConfigChange(String key, String value) {
// 记录配置变更日志
simulateProcess(1);
}
private void simulateProcess(int milliseconds) {
try {
Thread.sleep(milliseconds);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
8.2.3 Condition最佳实践:任务调度器
java
/**
* 基于Condition的任务调度器
* 特性:支持任务等待、超时控制、优雅关闭
*/
public class TaskScheduler {
private final PriorityBlockingQueue<Task> taskQueue = new PriorityBlockingQueue<>();
private final ReentrantLock lock = new ReentrantLock();
private final Condition taskAvailable = lock.newCondition();
private final Condition schedulerStopped = lock.newCondition();
private volatile boolean running = true;
private final Thread workerThread;
public TaskScheduler() {
this.workerThread = new Thread(this::processTasks, "TaskScheduler-Worker");
this.workerThread.start();
}
/**
* 提交任务 - 支持超时
*/
public boolean submitTask(Task task, long timeout, TimeUnit unit)
throws InterruptedException {
lock.lock();
try {
long deadline = System.nanoTime() + unit.toNanos(timeout);
// 等待直到有空间或超时
while (taskQueue.size() >= 1000) { // 队列容量限制
long remaining = deadline - System.nanoTime();
if (remaining <= 0) {
return false; // 超时
}
if (!taskAvailable.await(remaining, TimeUnit.NANOSECONDS)) {
return false; // 超时
}
}
taskQueue.offer(task);
taskAvailable.signal(); // 通知工作线程
return true;
} finally {
lock.unlock();
}
}
/**
* 立即提交任务(非阻塞)
*/
public boolean submitTaskNow(Task task) {
lock.lock();
try {
if (taskQueue.size() >= 1000) {
return false; // 队列已满
}
taskQueue.offer(task);
taskAvailable.signal();
return true;
} finally {
lock.unlock();
}
}
/**
* 优雅关闭 - 等待所有任务完成
*/
public void shutdown() throws InterruptedException {
lock.lock();
try {
running = false;
taskAvailable.signalAll(); // 唤醒工作线程
// 等待任务队列清空
while (!taskQueue.isEmpty()) {
schedulerStopped.await(1, TimeUnit.SECONDS);
}
} finally {
lock.unlock();
}
workerThread.join();
}
/**
* 立即关闭
*/
public void shutdownNow() {
lock.lock();
try {
running = false;
taskQueue.clear();
taskAvailable.signalAll();
} finally {
lock.unlock();
}
}
private void processTasks() {
while (running || !taskQueue.isEmpty()) {
try {
Task task = taskQueue.poll(1, TimeUnit.SECONDS);
if (task != null) {
executeTask(task);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
// 通知关闭完成
lock.lock();
try {
schedulerStopped.signalAll();
} finally {
lock.unlock();
}
}
private void executeTask(Task task) {
try {
task.execute();
} catch (Exception e) {
System.err.println("任务执行失败: " + e.getMessage());
}
}
public static class Task implements Comparable<Task> {
private final Runnable runnable;
private final long scheduledTime;
private final int priority;
public Task(Runnable runnable, long delay, TimeUnit unit, int priority) {
this.runnable = runnable;
this.scheduledTime = System.nanoTime() + unit.toNanos(delay);
this.priority = priority;
}
public void execute() {
runnable.run();
}
@Override
public int compareTo(Task other) {
int timeCompare = Long.compare(this.scheduledTime, other.scheduledTime);
if (timeCompare != 0) {
return timeCompare;
}
return Integer.compare(other.priority, this.priority); // 优先级高的在前
}
}
}
8.2.4 LockSupport最佳实践:自定义同步器
java
/**
* 基于LockSupport的自定义闭锁
* 特性:一次性屏障,支持超时和中断
*/
public class SimpleCountDownLatch {
private volatile int count;
private final Queue<Thread> waiters = new ConcurrentLinkedQueue<>();
public SimpleCountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("计数不能为负");
this.count = count;
}
/**
* 等待闭锁打开
*/
public void await() throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
if (count <= 0) {
return; // 已经打开
}
// 使用LockSupport阻塞
Thread current = Thread.currentThread();
waiters.offer(current);
// 双重检查避免错过信号
if (count > 0) {
LockSupport.park(this);
}
// 检查是否被中断唤醒
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
/**
* 超时等待
*/
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
if (count <= 0) {
return true;
}
long deadline = System.nanoTime() + unit.toNanos(timeout);
Thread current = Thread.currentThread();
waiters.offer(current);
try {
while (count > 0) {
long remaining = deadline - System.nanoTime();
if (remaining <= 0) {
return false; // 超时
}
LockSupport.parkNanos(this, remaining);
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
return true;
} finally {
waiters.remove(current);
}
}
/**
* 计数减1
*/
public void countDown() {
if (count <= 0) {
return; // 已经打开
}
if (--count == 0) {
// 唤醒所有等待线程
Thread waiter;
while ((waiter = waiters.poll()) != null) {
LockSupport.unpark(waiter);
}
}
}
/**
* 获取当前计数
*/
public int getCount() {
return count;
}
}
8.3 性能优化和陷阱避免
8.3.1 锁性能优化技巧
java
/**
* 锁性能优化实践
*/
public class LockPerformanceOptimization {
private final ReentrantLock lock = new ReentrantLock();
private int counter = 0;
private long lastLogTime = System.currentTimeMillis();
// ❌ 锁粒度太粗
public void coarseGrainedLock() {
lock.lock();
try {
// 包含非临界区操作
loadFromDatabase(); // 耗时IO操作
counter++; // 真正的临界区
saveToDatabase(); // 耗时IO操作
} finally {
lock.unlock();
}
}
// ✅ 锁粒度细化
public void fineGrainedLock() {
// 在锁外执行IO操作
loadFromDatabase();
lock.lock();
try {
counter++; // 只保护真正的临界区
} finally {
lock.unlock();
}
saveToDatabase(); // 在锁外执行IO操作
}
// ✅ 使用原子变量替代锁
private final AtomicInteger atomicCounter = new AtomicInteger(0);
public void atomicOperation() {
atomicCounter.incrementAndGet(); // 无锁操作,性能更高
}
// ✅ 读写分离
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();
private volatile boolean initialized = false;
public void lazyInit() {
if (!initialized) {
writeLock.lock();
try {
// 双重检查
if (!initialized) {
performInitialization();
initialized = true;
}
} finally {
writeLock.unlock();
}
}
// 后续读操作使用读锁
readLock.lock();
try {
useInitializedResource();
} finally {
readLock.unlock();
}
}
private void loadFromDatabase() {
// 模拟数据库加载
}
private void saveToDatabase() {
// 模拟数据库保存
}
private void performInitialization() {
// 初始化操作
}
private void useInitializedResource() {
// 使用初始化后的资源
}
}
8.3.2 常见陷阱及避免方法
java
/**
* 并发编程常见陷阱及解决方案
*/
public class ConcurrentPitfalls {
// 陷阱1:在锁内调用外部方法(可能发生死锁)
public void trap1() {
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
externalMethod(); // 危险!可能获取其他锁
} finally {
lock.unlock();
}
}
// 解决方案:减少锁内代码,只包含必要的临界区操作
public void solution1() {
ReentrantLock lock = new ReentrantLock();
// 在锁外准备数据
Object data = prepareData();
lock.lock();
try {
// 只执行必须同步的操作
updateSharedState(data);
} finally {
lock.unlock();
}
}
// 陷阱2:忘记在finally中释放锁
public void trap2() {
ReentrantLock lock = new ReentrantLock();
lock.lock();
if (someCondition()) {
return; // 忘记释放锁!
}
lock.unlock();
}
// 解决方案:使用try-finally模板
public void solution2() {
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
if (someCondition()) {
return;
}
// 其他操作
} finally {
lock.unlock(); // 确保释放
}
}
// 陷阱3:在Condition.await()中使用if而不是while
public void trap3() throws InterruptedException {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
boolean conditionMet = false;
lock.lock();
try {
if (!conditionMet) { // 错误!应该用while
condition.await();
}
// 可能被虚假唤醒,条件仍未满足
} finally {
lock.unlock();
}
}
// 解决方案:总是使用while循环检查条件
public void solution3() throws InterruptedException {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
boolean conditionMet = false;
lock.lock();
try {
while (!conditionMet) { // 正确!防止虚假唤醒
condition.await();
}
// 条件确定满足
} finally {
lock.unlock();
}
}
private boolean someCondition() {
return Math.random() > 0.5;
}
private Object prepareData() {
return new Object();
}
private void updateSharedState(Object data) {
// 更新共享状态
}
private void externalMethod() {
// 可能获取其他锁的外部方法
}
}
8.4 监控和调试技巧
8.4.1 锁监控工具
java
/**
* 锁监控和诊断工具
*/
public class LockMonitor {
/**
* 监控锁竞争情况
*/
public static void monitorLockContention(ReentrantLock lock, String lockName) {
new Thread(() -> {
while (true) {
try {
Thread.sleep(5000); // 每5秒检查一次
System.out.printf("锁[%s]监控: %n", lockName);
System.out.printf(" - 等待队列长度: %d%n", lock.getQueueLength());
System.out.printf(" - 是否有等待线程: %b%n", lock.hasQueuedThreads());
System.out.printf(" - 是否被当前线程持有: %b%n", lock.isHeldByCurrentThread());
System.out.printf(" - 重入次数: %d%n", lock.getHoldCount());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}, "LockMonitor-" + lockName).start();
}
/**
* 死锁检测
*/
public static void detectDeadlock() {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadMXBean.findDeadlockedThreads();
if (threadIds != null) {
System.err.println("检测到死锁!");
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadIds);
for (ThreadInfo threadInfo : threadInfos) {
System.err.println("死锁线程: " + threadInfo.getThreadName());
System.err.println("阻塞对象: " + threadInfo.getLockName());
System.err.println("阻塞者: " + threadInfo.getLockOwnerName());
}
}
}
/**
* 锁性能统计
*/
public static class LockStats {
private final ReentrantLock lock;
private long lockAcquireCount = 0;
private long totalWaitTime = 0;
private long maxWaitTime = 0;
public LockStats(ReentrantLock lock) {
this.lock = lock;
}
public void recordLockAcquire(long waitTime) {
lockAcquireCount++;
totalWaitTime += waitTime;
maxWaitTime = Math.max(maxWaitTime, waitTime);
}
public void printStats() {
System.out.printf("锁统计: %n");
System.out.printf(" - 获取次数: %d%n", lockAcquireCount);
System.out.printf(" - 平均等待时间: %.2fms%n",
lockAcquireCount > 0 ? (double)totalWaitTime / lockAcquireCount : 0);
System.out.printf(" - 最大等待时间: %dms%n", maxWaitTime);
System.out.printf(" - 当前等待线程: %d%n", lock.getQueueLength());
}
}
}
总结:正确使用并发锁的黄金法则
- 明确性:清楚知道每个锁保护的是什么数据
- 最小化:锁粒度尽可能小,持有时间尽可能短
- 一致性:按照固定顺序获取多个锁
- 可靠性:总是在finally块中释放锁
- 可中断性:对长时间操作使用可中断的锁获取方式
- 监控性:对关键锁进行监控和统计
Java并发包中的锁机制通过精妙的设计实现了高效、灵活的线程同步:
- AQS是基石:提供了同步队列管理和状态控制的基础设施
- Lock接口是门面:定义了丰富的锁操作API
- 重入锁解决重入问题:支持同一线程多次获取锁
- 读写锁优化读多写少场景:通过锁分离提升并发性能
- LockSupport提供底层阻塞能力:线程控制的精准工具
- Condition实现精确线程协调:多条件等待队列支持复杂同步逻辑
记住这个核心关系:Lock → AQS → Condition → LockSupport,它们共同构成了Java并发锁机制的完整体系。理解这些组件的内部机制,能够帮助我们正确选择和使用合适的同步工具,诊断和解决复杂的并发问题,设计高性能的并发组件。
记住这些原则和最佳实践,你就能构建出高效、可靠的并发程序。并发编程虽然复杂,但通过正确的工具和方法,完全可以驾驭!