7.减法计数器-加法计数器-信号量
1CountDownLatch(减法计数器)
1.CountDownLatch:减法记数器
有三个重要方法:
1.初始化,并确定计数器最大值
CountDownLatch countDownLatch = new CountDownLatch(6);
2.计数器数量-1
countDownLatch.countDown();
3.等待计数器归0,然后再往下执行
countDownLatch.await();
样例代码如下:
public class CountDownLatch_Test {
public static void main(String[] args) throws InterruptedException {
//重点1:创建CountDownLatch减法计数器,初始值为6
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
//重点2:每个线程间隔2秒启动
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
countDownLatch.countDown();
System.out.println(Thread.currentThread().getName() + "当前时间:" + DateTime.now() + ":数量减一 当前数量:" + countDownLatch.getCount());
}, "线程" + i).start();
}
//重点3:等待记数器归0,然后往下执行
countDownLatch.await();
System.out.println("所有线程等待");
}
}
输出:
线程0当前时间:2021-08-04 22:52:54:数量减一 当前数量:6
线程1当前时间:2021-08-04 22:52:56:数量减一 当前数量:5
线程2当前时间:2021-08-04 22:52:58:数量减一 当前数量:4
线程3当前时间:2021-08-04 22:53:00:数量减一 当前数量:3
线程4当前时间:2021-08-04 22:53:02:数量减一 当前数量:2
线程5当前时间:2021-08-04 22:53:04:数量减一 当前数量:1
所有线程等待
注意点:这里不是等待所有线程都执行完毕后再执行countDownLatch.await();后的方法
而是等计数器归0,即执行了countDownLatch.countDown();后,将记数器归0,切记!
所以需要等线程都执行完毕后再执行,可以将countDownLatch.countDown()方法放在每个线程的最后!
场景:需要多个线程执行完毕/或者启动某些线程后,才能执行后续代码!
2.CyclicBarrier(加法计数器)
加法记数器:
主要方法:
1.构造方法,第一个参数是从0加到多少时,会执行第二个参数中的方法
CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{})
2.该方法,底层会调用--count,但是也会堵塞该线程,cyclicBarrier.await()后面的代码会等CyclicBarrier条件满足后再一起执行,看下执行结果!
cyclicBarrier.await();
样例代码如下:
public class CyclicBarrier_Test {
public static void main(String[] args) throws InterruptedException {
//重点1:构造方法,如果加法计数器上达到最大值7时,会执行下面的输出方法
CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{
System.out.println("召唤神龙成功!");
});
for (int i = 0; i < 7; i++){
TimeUnit.SECONDS.sleep(3);
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+":时间:"+ DateTime.now()+" 当前数量:"+cyclicBarrier.getNumberWaiting());
//重点2:该方法底层会调用--count,但是会阻塞住该队列,等条件满足后,会一起执行后续方法!
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName()+"等待完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},"线程:"+i).start();
}
}
}
输出:
线程:0:时间:2021-08-04 23:15:45 当前数量:0
线程:1:时间:2021-08-04 23:15:48 当前数量:1
线程:2:时间:2021-08-04 23:15:51 当前数量:2
线程:3:时间:2021-08-04 23:15:54 当前数量:3
线程:4:时间:2021-08-04 23:15:57 当前数量:4
线程:5:时间:2021-08-04 23:16:00 当前数量:5
线程:6:时间:2021-08-04 23:16:03 当前数量:6
召唤神龙成功!
线程:6等待完毕!
线程:0等待完毕!
线程:1等待完毕!
线程:2等待完毕!
线程:4等待完毕!
线程:3等待完毕!
线程:5等待完毕!
3.Semaphore(信号量)
常用方法:类似于线程池的概念
1.获取线程资源
semaphore.acquire();
2.释放线程资源:
semaphore.release();
有两个目的:
1.用于多种共享资源的互斥使用
2.用于并发线程的控制
样例代码如下:
public class Semaphore_Test {
public static void main(String[] args) {
//类比停车场:3个停车位,有六辆车
//重点1:规定同时访问的线程数
Semaphore semaphore = new Semaphore(3);
for (int i =0; i < 6; i++){
new Thread(()->{
//1.获取停车位
try {
//重点2:获取线程资源
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"得到车位,停2秒!");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName()+"开走了..");
//重点3:释放线程资源
semaphore.release();
}
},"线程"+i).start();
}
}
}
输出:
线程2得到车位,停2秒!
线程0得到车位,停2秒!
线程1得到车位,停2秒!
线程0开走了..
线程3得到车位,停2秒!
线程1开走了..
线程4得到车位,停2秒!
线程2开走了..
线程5得到车位,停2秒!
线程5开走了..
线程4开走了..
线程3开走了..
结论:
发现只能有3个线程同时访问,其他的等待!
作用:
1.多个共享资源互斥的使用!
2.并发限流,控制最大额线程数!