一、CountDownLatch介绍
CountDownLatch是juc并发包下的一个同步工具类,用于协调多个线程之间同步的或者说线程之间的通信,告诉某一线程等待其他线程执行完后再执行,无法起到互斥作用;
线程计数器,创建对象时可初始化线程计数值,每当执行完一个线程可调用对应的方法计数器减1,当计数器为0零被中断的线程开始运行。
用法:当需要多个线程并行结束后再执行最终线程,例如计算多个线程并行的运行效率,或者线程A计算公司支出,线程B计算公司盈利,则最后线程C等待线程A/B执行完毕后即可计算出公司利润。
缺点:CountDownLatch是一次性的,计数器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。
二、简单演示:
一个线程计算公司支出费用;
一个线程计算公司营业额;
最后计算公司净利润;
package com.cjy.juc;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class CountDownLatchDemo {
public static void main(String[] args) {
Company company = new Company();
//初始化计数线程,与实际执行线程等,不然可能造成await()的线程阻塞;
CountDownLatch latch = new CountDownLatch(2);
PayThread t1 = new PayThread(company, latch);
ProfitThread t2 = new ProfitThread(company, latch);
new Thread(t1).start();
new Thread(t2).start();
try {
latch.await();//调用线程被挂起
} catch (InterruptedException e) {
}
double pay = company.getPay();
double profit = company.getProfit();
System.out.println("2018年夏季支出费用:"+pay+"万元");
System.out.println("2018年夏季营业额:"+profit+"万元");
System.out.println("2018年夏季净利润:"+(profit-pay)+"万元");
}
}
//公司
class Company{
private double pay;
private double profit;
public double getPay() {
return pay;
}
public void setPay(double pay) {
this.pay = pay;
}
public double getProfit() {
return profit;
}
public void setProfit(double profit) {
this.profit = profit;
}
}
//计算支出线程
class PayThread implements Runnable{
private volatile Company cy;
private CountDownLatch latch;
PayThread(Company cy,CountDownLatch latch){
this.cy=cy;
this.latch=latch;
}
@Override
public void run() {
try{
double pay = cy.getPay();
for (int i = 0; i < 800; i++) {
pay+=Math.random()*i;
}
cy.setPay(pay);
}finally{
latch.countDown();
}
}
}
//计算盈利线程
class ProfitThread implements Runnable{
private volatile Company cy;
private CountDownLatch latch;
ProfitThread(Company cy,CountDownLatch latch){
this.latch=latch;
this.cy=cy;
}
@Override
public void run() {
try{
double profit = cy.getProfit();
for (int i = 0; i < 1000; i++) {
profit+=Math.random()*i;
}
cy.setProfit(profit);
}finally{
latch.countDown();
}
}
}
三、CountDownLatch常用方法:
//构造函数,count与实际执行线程对应,如这里创建时count=100,那么就要有100个子线程来调用countDown()方法,不然被await()的线程又没有设置等待时间参数将不会执行一直处于阻塞状态
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
//计算器减1,一般在每个线程执行结束后调用一次
public void countDown() {
sync.releaseShared(1);
}
//调用线程处于阻塞状态,等待计数器为0时才会执行,这是一个重载函数;
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//设置等待时间,假如设置等待时间为5秒,则不等计数器为0时超过5秒后即会继续执行
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
//计数器当前值
public long getCount() {
return sync.getCount();
}