对于Java常见的并发工具包,有以下五种分别有不同的应用场景:
- CountDownLatch:适用于控制
多个线程同时开始执行 - Semaphore:适用于控制同时访问某个资源的
线程数量 - CyclicBarrier:适用于控制多个线程在
某个点上同步 - ReentrantLock:适用于控制多个线程对
共享资源的访问 - ConcurrentHashMap:适用于控制多个线程对
共享Map的访问
下面以常见的秒杀场景为例:
- CountDownLatch:CountDownLatch用于控制多个线程同时开始执行,适用于秒杀商品场景中的多个用户同时抢购。
当所有用户都准备好后,CountDownLatch会释放所有线程,让它们同时开始执行。
public class SeckillService {
private CountDownLatch startSignal = new CountDownLatch(1);
private CountDownLatch endSignal;
public void seckill() throws InterruptedException {
// 等待所有线程准备好
startSignal.await();
// TODO执行秒杀逻辑...
// 通知其他线程秒杀已结束
endSignal.countDown();
}
public void start(int threadCount) {
endSignal = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
seckill();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
// 通知所有线程准备好
startSignal.countDown();
try {
// 等待所有线程执行完毕
endSignal.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- Semaphore:Semaphore可用于控制同时访问某个资源的线程数量,
适用于秒杀商品场景中的限流。当达到最大并发数时,Semaphore会阻塞后续的线程,直到有线程释放资源。
public class SeckillService {
private Semaphore semaphore = new Semaphore(100);
public void seckill() throws InterruptedException {
// 获取许可证
semaphore.acquire();
// 执行秒杀逻辑
// 释放许可证
semaphore.release();
}
}
- CyclicBarrier:CyclicBarrier可以用于控制多个线程在某个点上同步,适用于秒杀商品场景中的
等待所有用户准备好后再开始抢购。当所有线程都到达指定点后,CyclicBarrier会释放所有线程,让它们继续执行。
public class SeckillService {
private CyclicBarrier barrier = new CyclicBarrier(100);
public void seckill() throws InterruptedException, BrokenBarrierException {
// 等待所有线程准备好
barrier.await();
// 执行秒杀逻辑
}
}
- ReentrantLock:ReentrantLock可以用于控制多个线程对共享资源的访问,适用于秒杀商品场景中的对
商品库存的访问。当一个线程获取到锁后,其他线程需要等待该线程释放锁后才能访问共享资源。
public class SeckillService {
private ReentrantLock lock = new ReentrantLock();
public void seckill() throws InterruptedException {
lock.lock();
try {
// 执行秒杀逻辑
} finally {
lock.unlock();
}
}
}
- ConcurrentHashMap:ConcurrentHashMap可以用于控制多个线程对共享Map的访问,适用于秒杀商品场景中的对
用户抢购记录的访问。ConcurrentHashMap可以保证多个线程同时访问Map时的线程安全。
public class SeckillService {
private ConcurrentHashMap<Long, Integer> map = new ConcurrentHashMap<>();
public void seckill(long userId) {
// 判断用户是否已经抢购过
if (map.containsKey(userId)) {
// 已经抢购过,直接返回
return;
}
// 执行秒杀逻辑
// 记录用户抢购记录
map.put(userId, 1);
}
}