JUC 并发工具类

ReentrantLock(可重入的独占锁)

应用

  1. 解决多线程竞争
  2. 实现多线程的顺序执行
  3. 实现多线程等待 / 通知机制

使用范式

1
2
3
4
5
6
lock.lock();
try{
}finally{
// 防止异常不释放锁
lock.unlock()
}

场景

** 递归 **、调用同类中方法、锁嵌套

抢票

结合 Condition 实现生产者消费者模式

Conditon

有 await()阻塞,signal() 唤醒,等待唤醒机制

机制
  • 生产者会在消息队列满了之后阻塞,生产后唤醒消费者消费
  • 消费者会在消息队列空了之后阻塞,消费后唤醒生产者生产
核心代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 队列定义
T[]items=new Object[size];
int count=0;
int putIndex=0;
int takeIndex=0;
int size=0;
Lock lock=new ReentrantLock();
Condition notEmpty=lock.newCondition;
Condition notFull=lock.newCondition;

// 生产者
public void put(e){
lock.lock();
try{
while(count==size){
notFull.await();
}
if(++index==size){
index=0;
}
item[index]=e;
++count;
notEmpty.signalAll();
}finally{
lock.unlock;
}
}

// 消费者
public T take(){
lock.lock();
try{
while(count==size){
notEmpty.await();
}
if(++takeIndex==size){
takeIndex=0;
}
item[takeIndex]=null;
--count;
notfull.signalAll();
}finally{
lock.unlock;
}
}

公平锁和非公平锁

  • 公平锁,不可以插队获取锁
  • 非公平锁,可以插队获取锁

Semaphore(信号量)

应用

控制同时访问某资源的线程数量,这也是共享锁,同时可以多个线程使用

场景

限流

实现并发访问量,控制流量

资源池

实现资源池,维护一组有限资源(比如数据库连接池,限定连接对象 50 个)

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 初始化, 最多 2 个线程同时执行
private static Semaphore sem=new Semaphore(2);
// 调用
fun getProductInfo(){
// 也可以用尝试获取申请,未获取到即被限流
// if(semaphore.tryAcquire()){
// }
try{
semaphore.acquire(); // 申请许可
}catch(){
}finally{
semaphore.release(); // 释放许可
}
}

CountDownLatch(闭锁)

应用

同步协助,允许线程等待,直到其他线程完成才一起操作

场景

  1. 并行任务同步
  2. 多任务汇总
  3. 资源初始化

实际场景

  1. 百米跑步,同时开跑
  2. 统一交卷
  3. 商品详情页数据汇总

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 百米跑步
static CountDownLatch begin=new CountDownLatch(1);
static CountDownLatch end=new CountDownLatch(8);
fun run(){
sout(" 准备 ");
begin.await();
sout(" 跑步 ");
sout(" 到达终点 ");
end.countDown();
}
fun main(){
for(i 0..8){
new Thread.start();
}
// 等待 -1,计数器为 0 即唤醒所有阻塞线程,开始跑步
begin.countDown();
// 开始等待,等每个线程 -1,计数器为 0 即所有线程跑完,唤醒主线程,结束比赛
end.await();
sout(" 结束比赛 ")
}

// 成绩汇总,同上 end 方式

CyclicBarrier

应用

对 CountDownLatch 优化,同 end 方式,但能重复使用

但如果没置零,则会一直阻塞

场景

  • 多线程任务
  • 数据处理

实际场景

  • 人满场景
  • 批量数据处理

Exchanger

两个线程用于交换数据

场景

  • 数据交换
  • 数据采集

Phaser

对 CountDownLatch 和 CyclicBarrier 优化,同 end 方式,但可以控制每个阶段的完成数量,

场景

比如公司团建爬山、吃饭、唱歌

三个阶段中,都可以让线程退出或加入