-
CountDownLatch(计数器)
1)简介
CountDownLatch是一个计数器闭锁,通过它可以完成类似于阻塞当前线程的功能,即:一个线程或多个线程一直等待,直到其他线程执行的操作完成。CountDownLatch用一个给定的计数器来初始化,该计数器的操作是原子操作,即同时只能有一个线程去操作该计数器。调用该类await方法的线程会一直处于阻塞状态,直到其他线程调用countDown方法使当前计数器的值变为零,每次调用countDown计数器的值减1。当计数器值减至零时,所有因调用await()方法而处于等待状态的线程就会继续往下执行。这种现象只会出现一次,因为计数器不能被重置,如果业务上需要一个可以重置计数次数的版本,可以考虑使用CycliBarrier。
2)使用场景
在某些业务场景中,程序执行需要等待某个条件完成后才能继续执行后续的操作;典型的应用如并行计算,当某个处理的运算量很大时,可以将该运算任务拆分成多个子任务,等待所有的子任务都完成之后,父任务再拿到所有子任务的运算结果进行汇总。
3)源码
//count参数为计数器的起始值
public CountDownLatch(int count) { };
//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public void await() throws InterruptedException { };
//和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
//将count值减1
public void countDown() { };
4)实例
CountDownLatch实现一个线程等待一组线程执行完毕后再开始执行:
static class ThreadWithThread_0 extends Thread{
private static int num = 5;
private static CountDownLatch countDownLatch = new CountDownLatch(num);
public void run() {
System.out.println(Thread.currentThread().getName() + "开始运行 --- 继承Thread");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " ".concat("运行完毕 --继承Thread"));
countDownLatch.countDown();
}
public static void main(String[] args) {
new Thread(){
public void run() {
System.out.println(Thread.currentThread().getName() + "等待其他线程运行完毕 --- 继承Thread");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " ".concat("其他线程运行完毕, 开始执行本线程 --继承Thread"));
}
}.start();
for (int i=0; i< num; i++) {
ThreadWithThread_0 threadWithThread = new ThreadWithThread_0();
threadWithThread.start();
}
}
}
执行结果
Thread-0等待其他线程运行完毕 --- 继承Thread
Thread-2开始运行 --- 继承Thread
Thread-4开始运行 --- 继承Thread
Thread-3开始运行 --- 继承Thread
Thread-1开始运行 --- 继承Thread
Thread-5开始运行 --- 继承Thread
Thread-2 运行完毕 --继承Thread
Thread-3 运行完毕 --继承Thread
Thread-4 运行完毕 --继承Thread
Thread-5 运行完毕 --继承Thread
Thread-1 运行完毕 --继承Thread
Thread-0 其他线程运行完毕, 开始执行本线程 --继承Thread
CountDownLatch实现一组线程批量等待某个状态, 然后在同时开始运行, 不过只能使用一次:
static class ThreadWithThread extends Thread{
private static int num = 5;
private static CountDownLatch countDownLatch = new CountDownLatch(num);
public void run() {
countDownLatch.countDown();
System.out.println("线程等待发令枪发令 --- 继承Thread");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " ".concat("继承Thread"));
}
public static void main(String[] args) {
for (int i=0; i< num; i++) {
ThreadWithThread threadWithThread = new ThreadWithThread();
threadWithThread.start();
}
}
}
执行结果
线程等待发令枪发令 --- 继承Thread
线程等待发令枪发令 --- 继承Thread
线程等待发令枪发令 --- 继承Thread
线程等待发令枪发令 --- 继承Thread
线程等待发令枪发令 --- 继承Thread
Thread-3 继承Thread
Thread-0 继承Thread
Thread-2 继承Thread
Thread-4 继承Thread
Thread-1 继承Thread
-
CyclicBarrie(回环栅栏)
1)简介
CyclicBarrier也是一个同步辅助类,它允许一组线程相互等待,直到到达某个公共屏障点(common barrier point)。通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行后面的操作。类似于CountDownLatch,它也是通过计数器来实现的。当某个线程调用await方法时,该线程进入等待状态,且计数器加1,当计数器的值达到设置的初始值时,所有因调用await进入等待状态的线程被唤醒,继续执行后续操作。因为CycliBarrier在释放等待线程后可以重用,所以称为循环barrier。CycliBarrier支持一个可选的Runnable,在计数器的值到达设定值后(但在释放所有线程之前),该Runnable运行一次,注,Runnable在每个屏障点只运行一个。
2)源码
//参数parties指让多少个线程等待至barrier状态, 参数barrierAction为当这些线程都达到barrier状态时会执行的内容。
public CyclicBarrier(int parties, Runnable barrierAction) {
}
//参数parties指让多少个线程等待至barrier状态
public CyclicBarrier(int parties) {
}
//挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务
public int await() throws InterruptedException, BrokenBarrierException { };
//这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
3)实例
CyclicBarrie实现一组线程等待某个状态, 然后同时开始执行, 可以使用多次:
static class ThreadWithThread_2 extends Thread{
private static int num = 5;
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(num);
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "等待全组线程准备完毕");
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + "等待完毕, 开始运行");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
for (int i=0; i< num; i++) {
ThreadWithThread_2 threadWithThread = new ThreadWithThread_2();
threadWithThread.start();
}
}
}
执行结果
Thread-0等待全组线程准备完毕
Thread-1等待全组线程准备完毕
Thread-3等待全组线程准备完毕
Thread-2等待全组线程准备完毕
Thread-4等待全组线程准备完毕
Thread-4等待完毕, 开始运行
Thread-0等待完毕, 开始运行
Thread-1等待完毕, 开始运行
Thread-2等待完毕, 开始运行
Thread-3等待完毕, 开始运行
-
Semaphore(信号量)
1)简介
Semaphore与CountDownLatch相似,不同的地方在于Semaphore的值被获取到后是可以释放的,并不像CountDownLatch那样一直减到底。它也被更多地用来限制流量,类似阀门的 功能。如果限定某些资源最多有N个线程可以访问,那么超过N个主不允许再有线程来访问,同时当现有线程结束后,就会释放,然后允许新的线程进来。有点类似于锁的lock与 unlock过程。相对来说他也有两个主要的方法:通过acquire()获取一个访问许可证, 通过release()释放一个访问许可证.
2)源码
//permits参数是表示允许几个线程访问
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
//permits参数是表示允许几个线程访问, fair表示是否是公平的
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
3)实例
Semaphore实现一个资源同时只允许若干个线程同时访问:
static class ThreadWithThread_1 extends Thread{
private static int num = 8;
private static Semaphore semaphore = new Semaphore(4);
public void run() {
try {
semaphore.acquire();
System.out.println("获得一个许可证, 开始访问");
Thread.sleep(20000);
semaphore.release();
System.out.println("释放一个许可证");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
for (int i=0; i< num; i++) {
ThreadWithThread_1 threadWithThread = new ThreadWithThread_1();
threadWithThread.start();
}
}
}
执行结果
Thread-1获得一个许可证, 开始访问
Thread-0获得一个许可证, 开始访问
Thread-2获得一个许可证, 开始访问
Thread-3获得一个许可证, 开始访问
Thread-1释放一个许可证
Thread-4获得一个许可证, 开始访问
Thread-0释放一个许可证
Thread-5获得一个许可证, 开始访问
Thread-3释放一个许可证
Thread-6获得一个许可证, 开始访问
Thread-2释放一个许可证
Thread-7获得一个许可证, 开始访问
Thread-4释放一个许可证
Thread-5释放一个许可证
Thread-6释放一个许可证
Thread-7释放一个许可证
总结
- CountDownLatch主要是实现了1个或N个线程需要等待其他线程完成某项操作之后才能继续往下执行操作,描述的是1个线程或N个线程等待其他线程的关系。CyclicBarrier主要是实现了多个线程之间相互等待,直到所有的线程都满足了条件之后各自才能继续执行后续的操作,描述的多个线程内部相互等待的关系。
- CountDownLatch是一次性的,而CyclicBarrier则可以被重置而重复使用。
网友评论