1、CountDownLatch
1.1 简介
CountDownLatch
是java.util.concurrent
包下的一个工具类。
即:这个工具类保存一个数字,不同的线程可以操作这个工具类使得数字减一。简单理解为减法计数器。
CountDownLatch
主要有两个方法,当一个或多个线程调用await
方法时,这些线程会阻塞。 *其它线程调用countDown
方法会将计数器减 1 (调用countDown
方法的线程不会阻塞), 当计数器的值变为 0 时,因await
方法阻塞的线程会被唤醒,继续执行。
CountDownLatch
构造器必须输入一个 int 类型数字,作为计数值。
CountDownLatch
有两个核心方法:countDown()
和await()
。
1、countDown()
:计数值减一;
2、await()
:等待计数器归零,然后再向下执行;
1.2 测试
假设有 10 个同学,要离开教室,人都离开后才能关门。
先测试不调用countDownLatch.await();
的情况:
package pers.klb.myjuc;
import java.util.concurrent.CountDownLatch;
public class AuxiliaryClassTest {
public static void main(String[] args) throws InterruptedException {
// 计数值为 10
CountDownLatch countDownLatch = new CountDownLatch(10);
// 创建 10 个线程,每个线程都给这个计数值减一
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "--> 离开房间");
countDownLatch.countDown(); // 计数值减一
}, String.valueOf(i)).start();
}
// 等待计数器归零,若还没归零,则等待
// countDownLatch.await();
System.out.println("关门");
}
}
运行结果:
可见,人还没走完就关门了,增加一个等待:
package pers.klb.myjuc;
import java.util.concurrent.CountDownLatch;
public class AuxiliaryClassTest {
public static void main(String[] args) throws InterruptedException {
// 计数值为 10
CountDownLatch countDownLatch = new CountDownLatch(10);
// 创建 10 个线程,每个线程都给这个计数值减一
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "--> 离开房间");
countDownLatch.countDown(); // 计数值减一
}, String.valueOf(i)).start();
}
// 等待计数器归零,若还没归零,则等待
countDownLatch.await();
System.out.println("关门");
}
}
运行结果:
每次线程都调用countDownLatch.countDown()
进行减一操作,如果计数值还没归零,countDownLatch.await()
会等待,如果归零了,则继续执行。
2、CyclicBarrier
2.1 简介
CyclicBarrier
也是java.util.concurrent
包下的一个工具类:
可以简单理解为加法计数器。
CyclicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是:让一组线程到达一个屏障(也可以叫同步点)时被阻塞, 直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。线程通过CyclicBarrier
的await()
方法进入屏障。
它的构造器有两个参数:
第一个参数为线程个数,第二个参数是线程达到第一个参数后触发的方法。
它有一个核心的方法:await()
,它的作用是:假设线程数为 N ,每个线程都调用这个方法,当调用这个方法的线程数达到 N 后,触发函数式接口的方法。
2.2 测试
首先在构造器中设置最大线程数为 7,但是只创建了 6 个线程。
package pers.klb.myjuc;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class AuxiliaryClassTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙");
});
for (int i = 1; i <= 6; i++) {
int num = i;
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "-> 获取第" + num + "课龙珠");
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
运行结果:
可见,线程数没达到最大值 7,每个线程都处于await()
方法中等待。
如果线程数设置为 7:
package pers.klb.myjuc;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class AuxiliaryClassTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙");
});
for (int i = 1; i <= 7; i++) {
int num = i;
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "-> 获取第" + num + "课龙珠");
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
运行结果:
3、Semaphore
3.1 简介
Semaphore
是java.util.concurrent
包下的工具类。
在信号量上我们定义两种操作:
1、acquire
(获取) 当一个线程调用acquire
操作时,它要么通过成功获取信号量(信号量减1),要么一直等下去,直到有线程释放信号量,或超时。
2、release
(释放)实际上会将信号量的值加 1,然后唤醒等待的线程。
信号量主要用于两个目的:一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
3.2 测试
package pers.klb.myjuc;
import java.util.concurrent.*;
/**
* @program: MyJUC
* @description: 辅助类
* @author: Meumax
* @create: 2020-08-08 17:23
**/
public class AuxiliaryClassTest {
public static void main(String[] args) {
// 创建信号量,数量为 3
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // 获取信号量
System.out.println(Thread.currentThread().getName() + "抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // release() 释放
}
}, String.valueOf(i)).start();
}
}
}
运行结果: