转:
https://www.jianshu.com/p/e233bb37d2e6
countDownLatch是在java1.5被引入,跟它一起被引入的工具类还有CyclicBarrier、Semaphore、concurrentHashMap和BlockingQueue。
存在于java.util.cucurrent包下。
countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
countDownLatch类中只提供了一个构造器:
//参数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() { };
这是例子在一般的书籍上都有,比较初级的例子,实战意义不大。
public class CountDownLatchTest {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(2);
System.out.println("主线程开始执行…… ……");
//第一个子线程执行
ExecutorService es1 = Executors.newSingleThreadExecutor();
es1.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
}
});
es1.shutdown();
//第二个子线程执行
ExecutorService es2 = Executors.newSingleThreadExecutor();
es2.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
latch.countDown();
}
});
es2.shutdown();
System.out.println("等待两个线程执行完毕…… ……");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("两个子线程都执行完毕,继续执行主线程");
}
}
结果集:
主线程开始执行…… ……
等待两个线程执行完毕…… ……
子线程:pool-1-thread-1执行
子线程:pool-2-thread-1执行
两个子线程都执行完毕,继续执行主线程
public class Parallellimit {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
CountDownLatch cdl = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
CountRunnable runnable = new CountRunnable(cdl);
pool.execute(runnable);
}
}
}
class CountRunnable implements Runnable {
private CountDownLatch countDownLatch;
public CountRunnable(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
synchronized (countDownLatch) {
/*** 每次减少一个容量*/
countDownLatch.countDown();
System.out.println("thread counts = " + (countDownLatch.getCount()));
}
countDownLatch.await();
System.out.println("concurrency counts = " + (100 - countDownLatch.getCount()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
修改后的代码:
package juc;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchParallellimit {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
CountDownLatch cdl = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
CountRunnable runnable = new CountRunnable(cdl);
pool.execute(runnable);
}
cdl.await();
System.out.println("concurrency counts = " + (100 - cdl.getCount()));
pool.shutdown();
}
static class CountRunnable implements Runnable {
private CountDownLatch countDownLatch;
public CountRunnable(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
public void run() {
synchronized (countDownLatch) {
/*** 每次减少一个容量*/
countDownLatch.countDown();
System.out.println("thread counts = " + (countDownLatch.getCount()));
}
}
}
}
说明:
上面代码里的synchronized 用法有点奇怪,CountDownLatch 是线程安全的采用CAS算法,在countDown()后面打印getCount()感觉就是一个悖论。countDown()是线程体完成之后执行的,可是在后面又打印语句。
加synchronized 能让线程顺序打印出来。
countDown()方法添加在哪里?
CountDownLatch的countDown()方法必须要放在线程内部业务处理完成之后最后一段添加。
如果业务发生了异常怎么办?
捕获异常在finally执行countDown(),否则会导致主线程阻塞。
package juc;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchStudy {
public static void main(String[] args) {
final int SIZE = 10;
final CountDownLatch cdl = new CountDownLatch(SIZE);
for(int i=0;i < SIZE; i++){
new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(100);
int a = 1/0;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//执行完毕后再调用
//一定要捕获异常处理
cdl.countDown();
}
//System.out.println(Thread.currentThread().getName()+" : "+cdl.getCount());
}
}).start();
}
System.out.println(Thread.currentThread().getName()+" : "+cdl.getCount());
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("done");
}
}
如果有的线程countDown()没有完成,getCount不为0,,主线程会怎么样?
主线程会一直阻塞。
*CountDownLatch和CyclicBarrier区别:
1.countDownLatch是一个计数器,线程完成一个记录一个,计数器递减,只能只用一次
2.CyclicBarrier的计数器更像一个阀门,需要所有线程都到达,然后继续执行,计数器递增,提供reset功能,可以多次使用
countDownLatch类中只提供了一个构造器:
https://www.jianshu.com/p/e233bb37d2e6
https://www.cnblogs.com/Lee_xy_z/p/10470181.html