一、Lock体系
1. ReentrantLock(可重入锁)
Lock lock = new ReentrantLock();
lock.lock();
try {// 临界区代码
} finally {lock.unlock();
}
- 特点:可重入、支持公平/非公平策略
- 优势:可中断锁获取、定时锁等待
- 使用场景:替代synchronized需要更灵活控制的场景
1. 核心使用场景
(1) 需要可中断的锁获取
ReentrantLock lock = new ReentrantLock();try {// 支持响应中断的锁获取lock.lockInterruptibly();try {// 执行可能长时间运行的任务processCriticalSection();} finally {lock.unlock();}
} catch (InterruptedException e) {// 处理中断逻辑handleInterruption();
}
典型场景:
- 实现可取消任务
- 处理死锁恢复机制
- 响应式系统中断处理
(2) 精确超时控制
if (lock.tryLock(300, TimeUnit.MILLISECONDS)) {try {// 临界区操作} finally {lock.unlock();}
} else {// 执行替代逻辑fallbackOperation();
}
适用场景:
- 高并发系统的熔断机制
- 实时系统的时间敏感操作
- 分布式锁的本地模拟
(3) 公平锁需求
ReentrantLock fairLock = new ReentrantLock(true); // 公平锁public void fairAccess() {fairLock.lock();try {// 保证先到先得的访问顺序} finally {fairLock.unlock();}
}
适用场景:
- 交易撮合系统
- 订单处理队列
- 需要严格顺序执行的批处理
(4) 多条件变量
class BoundedBuffer {final Lock lock = new ReentrantLock();final Condition notFull = lock.newCondition();final Condition notEmpty = lock.newCondition();public void put(Object x) throws InterruptedException {lock.lock();try {while (count == items.length)notFull.await();// ... put logicnotEmpty.signal();} finally {lock.unlock();}}
}
典型应用:
- 生产者-消费者模式
- 阻塞队列实现
- 复杂状态管理
6. 常见误区
误区 1:认为 ReentrantLock 总是比 synchronized 快
- 在低竞争场景使用 synchronized
误区 2:忘记在 finally 中释放锁
总结建议
-
虚拟线程优先策略:
- 新项目直接使用 ReentrantLock
- 旧系统逐步替换关键路径的 synchronized
-
锁选择决策树:
if (需要可中断/超时 || 需要公平性 || 虚拟线程环境)→ 选择 ReentrantLock else if (简单同步 && 短期持有)→ 使用 synchronized else→ 评估其他并发工具(如 StampedLock)
-
监控指标:
- 锁等待时间(超过 10ms 需要告警)
- 虚拟线程固定率(目标 < 5%)
- 锁竞争频率(每秒竞争次数)
通过深入理解 ReentrantLock 的机制和虚拟线程的协同工作原理,开发者可以构建出更高性能、更易维护的并发系统。在实际项目中,建议结合 APM 工具(如 Micrometer)持续监控锁使用情况,实现动态调优。
2. ReentrantReadWriteLock(读写锁)
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();
- 读写分离:共享读锁(允许多线程并发读),独占写锁
- 锁降级机制:写锁可降级为读锁
- 适用场景:读多写少的数据结构(如缓存)
3. StampedLock(邮戳锁)
StampedLock stampedLock = new StampedLock();
long stamp = stampedLock.tryOptimisticRead();
// 读取共享变量
if (!stampedLock.validate(stamp)) {// 升级为悲观读stamp = stampedLock.readLock();// ...stampedLock.unlockRead(stamp);
}
- 三种模式:写锁、悲观读、乐观读
- 无重入特性,需防止死锁
- 性能优势:乐观读不阻塞写操作
4. LockSupport(线程阻塞工具)
Thread thread = new Thread(() -> {LockSupport.park();// 被唤醒后执行
});
thread.start();
LockSupport.unpark(thread);
- 基于许可证的线程控制
- 精准唤醒指定线程
- 底层Unsafe类实现
5. SpinLock(自旋锁实现示例)
public class SpinLock {private AtomicBoolean locked = new AtomicBoolean(false);public void lock() {while (!locked.compareAndSet(false, true)) {// 自旋等待}}public void unlock() {locked.set(false);}
}
- 适用场景:临界区代码执行时间极短
- 优点:避免线程上下文切换
- 缺点:CPU空转消耗资源
6. Condition(条件变量)
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();// 等待方
lock.lock();
try {condition.await();
} finally {lock.unlock();
}// 通知方
lock.lock();
try {condition.signal();
} finally {lock.unlock();
}
- 实现精准的线程等待/通知机制
- 支持多个等待队列
- 典型应用:阻塞队列实现
二、并发工具类
1. AbstractQueuedSynchronizer(AQS)
// 自定义同步器示例
class Mutex extends AbstractQueuedSynchronizer {protected boolean tryAcquire(int acquires) {return compareAndSetState(0, 1);}protected boolean tryRelease(int releases) {setState(0);return true;}
}
- CLH队列管理等待线程
- 模板方法设计模式
- 同步状态原子管理
2. CountDownLatch(倒计时门闩)
CountDownLatch latch = new CountDownLatch(3);// 工作线程
new Thread(() -> {// 完成任务latch.countDown();
}).start();// 主线程等待
latch.await();
- 一次性使用
- 典型应用:并行任务初始化
3. CyclicBarrier(循环屏障)
CyclicBarrier barrier = new CyclicBarrier(3, () -> {// 所有线程到达后执行
});new Thread(() -> {// 执行任务barrier.await();
}).start();
- 可重复使用
- 支持屏障动作
- 应用场景:多阶段并行计算
4. Exchanger(数据交换器)
Exchanger<String> exchanger = new Exchanger<>();new Thread(() -> {String data = exchanger.exchange("Thread1 Data");
}).start();new Thread(() -> {String data = exchanger.exchange("Thread2 Data");
}).start();
- 双线程数据交换
- 支持超时机制
- 应用场景:管道式处理
5. Phaser(阶段同步器)
Phaser phaser = new Phaser(3);new Thread(() -> {phaser.arriveAndAwaitAdvance();// 阶段1任务phaser.arriveAndDeregister();
}).start();
- 动态注册机制
- 分阶段任务控制
- 支持分层结构
6. Semaphore(信号量)
// 创建包含3个许可的信号量(公平模式)
Semaphore semaphore = new Semaphore(3, true);// 获取许可(阻塞方式)
semaphore.acquire();
try {// 访问共享资源(最多3个线程并发)
} finally {semaphore.release();
}// 非阻塞尝试获取
if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) {try {// 临界区操作} finally {semaphore.release();}
}
核心特性:
-
资源池管理:
- 通过许可(permits)控制并发访问数量
- 许可数量可以动态调整(
reducePermits
/increasePermits
)
-
灵活获取方式:
- 支持批量获取(
acquire(int permits)
) - 提供可中断/不可中断获取方式
- 支持超时机制(
tryAcquire
)
- 支持批量获取(
-
公平性选择:
- 非公平模式(默认):吞吐量优先
- 公平模式:按请求顺序分配许可
典型应用场景:
- 连接池限流:
// 数据库连接池实现
public class ConnectionPool {private final Semaphore semaphore;private final BlockingQueue<Connection> pool;public ConnectionPool(int size) {this.semaphore = new Semaphore(size);this.pool = new ArrayBlockingQueue<>(size);// 初始化连接...}public Connection getConnection() throws InterruptedException {semaphore.acquire();return pool.take();}public void release(Connection conn) {pool.offer(conn);semaphore.release();}
}
- 限流控制系统:
// API限流控制器(每秒10个请求)
class RateLimiter {private final Semaphore semaphore = new Semaphore(10);private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);public RateLimiter() {scheduler.scheduleAtFixedRate(() -> {int available = semaphore.availablePermits();if (available < 10) {semaphore.release(10 - available);}}, 0, 1, TimeUnit.SECONDS);}public boolean tryAcquire() {return semaphore.tryAcquire();}
}
- 生产者-消费者模型:
// 有界缓冲区实现
class BoundedBuffer<E> {private final Semaphore availableItems;private final Semaphore availableSpaces;private final Queue<E> queue = new LinkedList<>();public BoundedBuffer(int capacity) {availableItems = new Semaphore(0);availableSpaces = new Semaphore(capacity);}public void put(E item) throws InterruptedException {availableSpaces.acquire();synchronized (this) {queue.offer(item);}availableItems.release();}public E take() throws InterruptedException {availableItems.acquire();E item;synchronized (this) {item = queue.poll();}availableSpaces.release();return item;}
}
使用技巧:
- 许可动态调整:
// 运行时动态扩展容量
void resize(int newCapacity) {int delta = newCapacity - semaphore.availablePermits();if (delta > 0) {semaphore.release(delta); // 扩容} else {semaphore.reducePermits(-delta); // 缩容}
}
- 死锁预防:
// 使用tryAcquire避免死锁
if (semaphore.tryAcquire(2, 100, TimeUnit.MILLISECONDS)) {try {// 临界区操作} finally {semaphore.release(2);}
} else {// 处理获取失败逻辑
}
- 与ReentrantLock对比:
特性 | Semaphore | ReentrantLock |
---|---|---|
资源控制 | 多许可控制 | 单锁独占 |
可重入性 | 不支持 | 支持 |
公平性 | 可配置 | 可配置 |
条件变量 | 无 | 支持Condition |
使用场景 | 资源池/流量控制 | 互斥操作 |
注意事项:
-
释放次数匹配:
- 确保acquire()与release()调用次数匹配
- 使用try-finally保证释放
-
不可重入特性:
- 同一线程多次acquire需要对应次数的release
-
性能监控:
- 通过availablePermits()监控系统负载
- 使用getQueueLength()检测等待线程数
-
资源泄漏预防:
- 建议使用带超时的tryAcquire
- 结合Thread.interrupt()处理阻塞线程
源码关键实现:
// 基于AQS的Sync内部类
abstract static class Sync extends AbstractQueuedSynchronizer {Sync(int permits) {setState(permits);}final int getPermits() {return getState();}final int nonfairTryAcquireShared(int acquires) {for (;;) {int available = getState();int remaining = available - acquires;if (remaining < 0 ||compareAndSetState(available, remaining))return remaining;}}protected final boolean tryReleaseShared(int releases) {for (;;) {int current = getState();int next = current + releases;if (next < current) // overflowthrow new Error("Maximum permit count exceeded");if (compareAndSetState(current, next))return true;}}
}
五、最佳实践(补充)
- Semaphore使用准则:
- 许可数量设置应基于系统压测结果
- 避免在持有信号量时执行阻塞操作
- 对不可靠资源访问添加finally释放块
- 结合监控系统实现动态配额调整
通过Semaphore的合理使用,可以有效解决资源池管理、流量控制、系统过载保护等典型并发问题。其灵活的许可管理机制使其成为构建高弹性系统的利器,但需注意避免因错误使用导致的线程饥饿或系统死锁问题。
三、高性能计数器
1. LongAdder
LongAdder counter = new LongAdder();
counter.increment();
long sum = counter.sum();
- 实现原理:
- 基础值 + Cell数组分散竞争
- 最终一致性保证
- 适用场景:高频写、低频读
2. DoubleAdder
DoubleAdder adder = new DoubleAdder();
adder.add(1.5);
double sum = adder.sum();
- 类似LongAdder的浮点版本
- 注意精度问题
- 适用场景:统计指标收集
3. 性能对比
计数器类型 | 写性能 | 读性能 | 内存消耗 | 适用场景 |
---|---|---|---|---|
AtomicLong | 低 | 高 | 低 | 读写平衡场景 |
LongAdder | 高 | 低 | 高 | 写多读少场景 |
四、选型策略
- 锁机制选择:
- 优先考虑synchronized
- 需要高级功能时选择ReentrantLock
- 读多写少场景使用StampedLock
- 并发工具选择:
- 一次性等待用CountDownLatch
- 多阶段任务用Phaser
- 线程数固定用CyclicBarrier
- 计数器选择:
- 普通场景使用AtomicLong
- 高并发写场景使用LongAdder
- 需要精确值时慎用DoubleAdder
五、最佳实践
- 避免锁嵌套使用
- 总是使用try-finally释放锁
- 合理设置超时时间
- 优先使用并发集合类
- 使用ThreadLocal减少竞争
- 监控锁竞争情况(JStack、JConsole)
本文深入剖析了Java并发编程的核心组件,开发者应根据具体场景选择最合适的工具。在高并发系统中,合理的锁选择和并发工具使用可以带来数量级的性能提升。建议结合JVM参数调优(如偏向锁、自旋参数)和并发监控工具进行综合优化。