8、常用辅助类
8.1、CountDownLatch
CountDownLatch:减计数器,使用场景:
乘坐飞机时,乘务服务员都要根据手中的订票人数清点人数,只有人数到齐了才关门起飞,那怎么有效精准的控制什么时候关门起飞呢,使用CountDownLatch减法计数器有效控制。
CountDownLatch 编码模型:
CountDownLatch countDownLatch = new CountDownLatch(6);
countDownLatch.countDown();
进去一个人(产生一个线程)计数器就 -1
countDownLatch.await();
阻塞等待计数器归零
package com.interview.concurrent.countdownlatch;
import java.util.concurrent.CountDownLatch;
/**
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @description 描述:减法计数器
* @date 2023/2/23 11:37
*/
public class CountDownLatchDemo {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(14); // 初始值14,有14个人需要上飞机
for (int i = 1; i <=14 ; i++) {
new Thread(()-{
System.out.println(Thread.currentThread().getName()+"进仓了一个人");
// 进去一个人计数器就减1
countDownLatch.countDown();
},String.valueOf(i)).start();
}
try {
countDownLatch.await(); // 阻塞等待计数器归零
} catch (InterruptedException e) {
e.printStackTrace();
}
// 阻塞的操作 : 计数器 num++
System.out.println(Thread.currentThread().getName()+"====人已到齐,关门起飞====");
}
}
使用CountDownLatch减法计数器,关门起飞永远都在最后一步完成。
有减法计数器,就一定有加法计数器,如下CyclicBarrier类。
8.2、CyclicBarrier
CyclicBarrier:加法计数器
CyclicBarrier编码模型:
1、 创建CyclicBarrier对象
CyclicBarrier cyclicBarrier = new CyclicBarrier(maxNum, new MyRunnable());
2、编写业务代码
3、cyclicBarrier.await(); //在线程里面等待阻塞,累加1,打到最大值maxNum时,触发MyRunnable执行
示例代码如下:
package com.interview.concurrent.cyclicbarrier;
import javax.swing.plaf.TableHeaderUI;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @description 描述:加法计数器
* 适用场景:满了才开始进行下一步操作,如下场景:
* 新浪微博连续签到10天,可得10积分。
* @date 2023/2/23 12:02
*/
public class CyclicBarrierDemo1 {
public static void main(String[] args) {
/**
* @description:
* 1、创建CyclicBarrier
* 等待cyclicBarrier计数器满,就执行后面的Runnable,不满就阻塞
* 注意,CyclicBarrier等待的是10个线程,而不是一个线程。
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new Runnable() {
@Override
public void run() {
System.out.println("恭喜您,连续签到10天,获得10积分!");
}
});
/**
* @description:
* 在main线程中循环了10次
*/
//cyclicBarrier(cyclicBarrier);
/**
* 创建10个线程
*/
cyclicBarrierThread(cyclicBarrier);
}
/**
* @description:
* 实际上就是在一个main线程中循环了10次,CycliBarrier等待的是10个线程,
* 所以,CycliBarrier不会执行Runnable,而是一直等待有10个线程进来才开始执行。
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @date 2023/2/23 12:24
*/
public static void cyclicBarrier(CyclicBarrier cyclicBarrier){
for (int i = 1; i < 10; i++) {
System.out.println("签到第" + i + "天");
try {
cyclicBarrier.await(); //等待阻塞
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
/**
* @description: 创建10个线程
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @date 2023/2/23 12:25
*/
public static void cyclicBarrierThread(CyclicBarrier cyclicBarrier){
for (int i = 1; i <= 10 ; i++) {
final int temp = i;
new Thread(() - {
System.out.println("签到第" + temp + "天");
try {
cyclicBarrier.await(); //等待阻塞
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
在一个线程循环10次,CyclicBarrier一直等待
创建10个线程,CyclicBarrier满后就执行Runnable。
总结:
CyclicBarrier和CountDownLatch非常相似,一个表示加法,一个表示减法,但也有不同之处:
1、CyclicBarrier只能唤起一个任务,CountDownLatch可以唤起多个任务。
2、CyclicBarrier可重用,CountDownLatch不可重用,计数值为0该CountDownLatch就不可再用了。
8.3、Semaphore
信号量通常用于限制线程数,而不是访问某些(物理或逻辑)资源。
Semaphore: 信号灯。 适用场景:限制资源,如抢位置、限流等。
Semaphore编码模型:
1、创建信号灯
Semaphore semaphore = new Semaphore(5); // 5个位置
2、等待获取信号灯
semaphore.acquire();//等待获取许可证
3、业务代码
4、释放信号
semaphore.release();//释放资源,车离开了,就要释放车位
示例代码如下:
package com.interview.concurrent.semaphore;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @description 描述:Semaphore:信号灯。适用场景:限制资源,如抢位置、限流等。
* 10辆车,抢占5个位置
* @date 2023/2/23 12:36
*/
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(5); // 5个位置
for (int i = 1; i < 10; i++) {
new Thread(() - {
try {
semaphore.acquire();//等待获取许可证
System.out.println(Thread.currentThread().getName() + "抢到了车位");
TimeUnit.SECONDS.sleep(10); //抢到的车,在这个车位上停放了10秒钟
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
//停放了10秒钟的车子离开
System.out.println(Thread.currentThread().getName() + "离开了一辆车");
semaphore.release();//释放资源,车离开了,就要释放车位
}
},String.valueOf(i)).start();
}
}
}
运行结果如下: