文章目录
三、工具类
1.Semaphore
1.1 简介
Semaphore是信号量,允许多个线程访问临界区,可以控制访问临界区的数量。实现逻辑为核心是初始化一个计数器表示可用许可的数目,请求资源时减去申请许可的个数,可用许可数目小于0时阻塞,释放资源时加上释放许可的个数,并唤醒对应个数的线程。通常用作控制池化资源(连接池、对象池)的访问,达到限流的效果。
1.2 方法介绍
/**
* 构造方法
* @param permits 可用许可的初始数目
*/
public Semaphore(int permits)
/**
* 构造方法
* @param permits 可用许可的初始数目
* @param fair 是否公平
*/
public Semaphore(int permits, boolean fair)
/**
* 请求资源
* @throws InterruptedException 中断异常
*/
public void acquire() throws InterruptedException
/**
* 请求给定数目的资源
* @param permits 请求的许可数目
* @throws InterruptedException
*/
public void acquire(int permits) throws InterruptedException
/**
* 释放资源
*/
public void release()
/**
* 释放给定数目的资源
* @param permits 释放的许可数目
*/
public void release(int permits)
1.3 使用样例
模拟抢车位的情况,共有两个车位,有五辆车在抢车位
public static class CarThread extends Thread {
private final Semaphore semaphore;
public CarThread(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始抢车位");
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"已抢到车位");
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"停车结束");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}
}
public static void main(String[] args){
//模拟只有两个车位的情况
Semaphore semaphore = new Semaphore(2, true);
for (int i = 0; i < 5; i++) {
new CarThread(semaphore).start();
}
}
1.4 注意事项
release不需在acquire后调用,release实际执行的是增加许可数量的操作与acquire没有强关联
2.CountDownLatch
2.1 简介
CountDownLatch是计数器,没有公平或不公平之分,使用await阻塞直到等待计数器变为0,变为0后所有await阻塞的线程都会唤醒,使用countDown使计数器减一,CountDownLatch的使用场景通常用于一个线程需要等待其他线程的执行结果的情况,如需等待所有运动员就绪才开始比赛
2.2 方法介绍
/**
* 构造方法
* @param count 初始化计数器数量
*/
public CountDownLatch(int count)
/**
* 计数器减一,为0时唤醒所有阻塞的线程
*/
public void countDown()
/**
* 阻塞线程
* @throws InterruptedException 中断异常
*/
public void await() throws InterruptedException
2.3 使用样例
主线程等待子线程的任务完成后执行剩下的步骤
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(2);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程一处理");
countDownLatch.countDown();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程二处理");
countDownLatch.countDown();
}
}).start();
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("处理完成");
}
3.CyclicBarrier
3.1 简介
CyclicBarrier是循环栅栏,调用一次await计数器减一,计数器为0后最后一个线程唤醒所有线程,并执行回调方法,重置计数器,开始下一个轮次(Generation),适用于线程间相互等待直到所有线程执行完毕后才继续执行的情况,如面试中等待所有人到齐才开始笔试,所有人笔试完成后才开始面试
3.2 方法介绍
/**
* 构造函数
* @param parties 参与者数量
* @param barrierAction 每一轮执行完毕回调函数
*/
public CyclicBarrier(int parties, Runnable barrierAction)
/**
* 构造函数
* @param parties 参与者数量
*/
public CyclicBarrier(int parties)
/**
* 阻塞等待并使计数器减一
* @return 当前线程的到达索引,其中索引getParties() - 1表示第一个到达,0表示最后一个到达
* @throws InterruptedException 中断异常
* @throws BrokenBarrierException 如果当前线程正在等待时,另一个线程被中断或超时,或者屏障被重置,或者在await被调用时屏障被打破,或者屏障操作(如果存在)由于异常而失败
*/
public int await() throws InterruptedException, BrokenBarrierException
/**
* 阻塞等待并使计数器减一,可超时
* @param timeout 超时时间
* @param unit 超时单位
* @return
* @throws InterruptedException 可中断异常
* @throws BrokenBarrierException 如果当前线程正在等待时,另一个线程被中断或超时,或者屏障被重置,或者在await被调用时屏障被打破,或者屏障操作(如果存在)由于异常而失败
* @throws TimeoutException 超时异常
*/
public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
3.3 使用样例
所有考生等待考试开始,并等待考试结束
public static class IntervieweeThread extends Thread{
private final CyclicBarrier cyclicBarrier;
public IntervieweeThread(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"到达考试地点,开始等待考试开始");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"做完试卷,等待考试结束");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//5个考生
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("本轮执行完毕");
}
});
for (int i = 0; i < 5; i++) {
new IntervieweeThread(cyclicBarrier).start();
}
}
3.4 注意事项
CyclicBarrier 会响应中断,被中断的线程会唤醒所有其他的被阻塞线程,计数器重置为初始值
4.Exchanger
4.1 简介
Exchanger是一个交换器,用于两个或多个线程之间相互交换数据,实现原理为一个线程判断slot为空后(数据交换槽)放入数据后阻塞,另一个线程判断slot不为空,放入数据唤醒数据对应的线程,并阻塞自己等待被唤醒。为了提高并发度,引入arena node数组,使多个线程间在不同的槽上进行访问,提高并发度。
4.2 方法介绍
/**
* 交换数据
* @param x 待交换的数据
* @return 交换回来的数据
* @throws InterruptedException
*/
public V exchange(V x) throws InterruptedException
4.3 使用样例
两个线程间交换数据
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<String>();
new Thread(new Runnable() {
@Override
public void run() {
try {
String data = exchanger.exchange("线程一数据");
System.out.println("线程一交换后的数据:"+data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
String data = exchanger.exchange("线程二数据");
System.out.println("线程二交换后的数据:"+data);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
5.Phaser
5.1 简介
Phaser是阶段器,一个可重用的同步屏障,在功能上类似于CyclicBarrier和CountDownLatch,但支持动态增删参与者的数量以及层级扩展(支持添加父Phaser)。如下图所示,每执行一个周期会记录phase的值,表示不同的执行阶段。
5.2 方法介绍
/**
* 构造方法
* 默认参与者数量为0
*/
public Phaser() {
this(null, 0);
}
/**
* 构造方法
*
* @param parties 参与者数量为0
*/
public Phaser(int parties) {
this(null, parties);
}
/**
* 构造方法
*
* @param parent 父Phaser 会自动向父Phaser注册自己
*/
public Phaser(Phaser parent) {
this(parent, 0);
}
/**
* 构造方法
*
* @param parent 父Phaser 会自动向父Phaser注册自己
* @param parties 参与者数量为0
*/
public Phaser(Phaser parent, int parties)
/**
* 注册一个新参与者使原数量+1
*
* @return
*/
public int register()
/**
* 批量注册若干个参与者 使原数量+parties
*
* @param parties 参与者数量
* @return
*/
public int bulkRegister(int parties)
/**
* 当前线程到达当前阶段 类似于countDown
*
* @return arrival phase number 阶段编号
*/
public int arrive()
/**
* 当前线程到达当前阶段并撤销注册
*
* @return phase阶段编号
*/
public int arriveAndDeregister()
/**
* 使当前线程到达phaser并等待其他任务到达,等价于awaitAdvance(arrive())。
* 如果需要等待中断或超时,可以使用awaitAdvance方法完成一个类似的构造。
* 如果需要在到达后取消注册,可以使用awaitAdvance(arriveAndDeregister())。
* arriveAndAwaitAdvance类似 CyclicBarrier await
*
* @return phase阶段编号
*/
public int arriveAndAwaitAdvance()
/**
* 阻塞当前阶段编号线程,编号不匹配不阻塞
* @param phase 当前 phase 阶段编号
* @return 下一个 phase 阶段编号
*/
public int awaitAdvance(int phase)
/**
* 可中断的阻塞
* @param phase 当前 phase 阶段编号
* @return 下一个 phase 阶段编号
* @throws InterruptedException 中断异常
*/
public int awaitAdvanceInterruptibly(int phase) throws InterruptedException
/**
* 超时阻塞
* @param phase 当前 phase 阶段编号
* @param timeout 超时时间
* @param unit 超时时间单位
* @return 下一个 phase 阶段编号
* @throws InterruptedException 中断异常
* @throws TimeoutException 超时异常
*/
public int awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException
/**
* 返回当前阶段号,终止时为负数,最大值为Integer.MAX_VALUE,超过最大值清零
* @return 当前阶段号
*/
public final int getPhase()
/**
* 当前phaser是否被终止
*/
public boolean isTerminated()
/**
* 终止当前phaser,已注册的parties不受影响,如果是分层结构,则终止所有phaser,通常用于异常后协调恢复
*/
public void forceTermination()
5.3 使用样例
5.3.1 替代CountDownLatch
public static void main(String[] args) {
Phaser phaser = new Phaser(2);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程一处理");
phaser.arrive();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程二处理");
phaser.arrive();
}
}).start();
phaser.awaitAdvance(phaser.getPhase());
System.out.println("处理完成");
}
5.3.2 替代CyclicBarrier
public static class IntervieweeThread extends Thread {
private final Phaser phaser;
public IntervieweeThread(Phaser phaser) {
this.phaser = phaser;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "到达考试地点,开始等待考试开始");
phaser.arriveAndAwaitAdvance();
System.out.println(Thread.currentThread().getName() + "做完试卷,等待考试结束");
phaser.arriveAndAwaitAdvance();
}
}
public static class MyPhaser extends Phaser {
public MyPhaser(int parties) {
super(parties);
}
/**
* 可重写方法,当一个阶段的所有线程都到达时,执行该方法
* @param phase 当前阶段
* @param registeredParties 当前的注册方数量
* @return 返回true时终止执行 返回false phase加1
*/
@Override
protected boolean onAdvance(int phase, int registeredParties) {
switch (phase) {
case 0 :
System.out.println("考试开始");
return false;
case 1:
System.out.println("考试结束");
return false;
default:
//终止
return true;
}
}
}
public static void main(String[] args) {
//5个考生
Phaser phaser = new MyPhaser(5);
for (int i = 0; i < 5; i++) {
new IntervieweeThread(phaser).start();
}
}
5.3.3 新特性
动态添加参与者及不等待其他线程直接执行
public static class IntervieweeThread extends Thread {
private final Phaser phaser;
public IntervieweeThread(Phaser phaser) {
this.phaser = phaser;
}
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "到达考场,等待其他同学到场");
phaser.arriveAndAwaitAdvance();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
phaser.arriveAndDeregister();
System.out.println(Thread.currentThread().getName() + "做完试卷,直接走");
}
}
public static void main(String[] args) {
//5个考生
Phaser phaser = new Phaser(5);
for (int i = 0; i < 5; i++) {
new IntervieweeThread(phaser).start();
}
//新来了一个考生
phaser.register();
new IntervieweeThread(phaser).start();
//新来了三个考生
phaser.bulkRegister(3);
new IntervieweeThread(phaser).start();
new IntervieweeThread(phaser).start();
new IntervieweeThread(phaser).start();
}
终止
public static void main(String[] args) {
Phaser phaser = new Phaser(2);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("阻塞等待 阶段"+phaser.getPhase());
phaser.arriveAndAwaitAdvance();
System.out.println("等待完成 阶段"+phaser.getPhase());
System.out.println("再次阻塞等待 阶段"+phaser.getPhase());
phaser.arriveAndAwaitAdvance();
System.out.println("再次等待完成 阶段"+phaser.getPhase());
}
}).start();
System.out.println("终止");
phaser.forceTermination();
可以看到当发出终止时,phase的值为负数,不会执行阻塞逻辑
终止
阻塞等待 阶段-2147483648
等待完成 阶段-2147483648
再次阻塞等待 阶段-2147483648
再次等待完成 阶段-2147483648