# countDownLatch(共享锁)
CountDownLatch是Java并发包下的一个工具类,latch是门闩的意思。
顾名思义,CountDownLatch就是有一个门闩挡住了里面的人(线程)出来,当count减到0的时候,门闩就打开了,人(线程)就可以出来了。
API
//CountDownLatch类只提供了一个构造器:参数count为计数值即state
//
public CountDownLatch(int count) {
};
//下面这3个方法是CountDownLatch类中最重要的方法:
//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
//可以多次调用 可以多次调用 可以多次调用 用于阻塞多个线程
public void await() throws InterruptedException { };
//将count值减1
//可以多次调用 可以多次调用 可以多次调用 用于减少构造器中的count值
public void countDown() { };
//和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
实现原理
1.创建计数器
当我们调用CountDownLatch countDownLatch=new CountDownLatch(4) 时候,此时会创建一个AQS的同步队列,并把创建CountDownLatch 传进来的count赋值给AQS队列的 state,所以state的值也代表CountDownLatch所剩余的计数次数。
2.阻塞线程
当我们调用countDownLatch.wait()的时候,会创建一个节点,加入到AQS阻塞队列,并同时把当前线程(主线程)挂起。
判断计数器是计数完毕,未完毕则把当前线程加入阻塞队列,挂起当前线程。
3.计数器递减
当我们调用countDownLatch.down()方法的时候,会对计数器进行减1操作,AQS内部是通过释放锁的方式,对state进行减1操作,当state=0的时候证明计数器已经递减完毕,此时会将AQS阻塞队列里的节点线程全部唤醒。
应用:并发等待
不要以为countDownLatch只能await一次。他可以await多次!
比如2个线程t3 t4 同时等待t1 t2完成。此时就可以利用CountDownLatch来实现这种功能了。
package org.apache.rocketmq.example.thread;
import java.util.concurrent.CountDownLatch;
public class Test {
public static void main(String[] args) {
//指定count=2
final CountDownLatch latch = new CountDownLatch(2);
Thread t1 = new Thread() {
public void run() {
try {
System.out.println("子线程" + Thread.currentThread().getName() + "正在执行");
Thread.sleep(3000);
System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕");
//count减少1
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
;
};
t1.start();
Thread t2 = new Thread() {
public void run() {
try {
System.out.println("子线程" + Thread.currentThread().getName() + "正在执行");
Thread.sleep(3000);
System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕");
//count减少1
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
;
};
t2.start();
Runnable t3 = new Runnable() {
@Override
public void run() {
try {
System.out.println("t3等待中");
//当count=0 会被唤醒
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t3等待完成");
}
};
new Thread(t3).start();
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("t4等待中");
//当count=0 会被唤醒
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t4等待完成");
}
});
t4.start();
System.out.println("执行主线程");
}
}
子线程Thread-0正在执行
子线程Thread-1正在执行
t3等待中
t4等待中
子线程Thread-0执行完毕
子线程Thread-1执行完毕
t3等待完成
t4等待完成
应用:压力测试
package com.xttblog.canal.test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
/**
* CasSingletonTest
* @author www.xttblog.com
* @date 2019/2/27 下午2:39
*/
public class CasSingletonTest {
public static AtomicInteger objectcount = new AtomicInteger();
public static void main(String[] args) throws InterruptedException {
final CountDownLatch begin = new CountDownLatch(1);
final CountDownLatch last = new CountDownLatch(1000);
for(int i=0;i<1000;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
//1.所有的线程都会阻塞在这
begin.await();
System.out.println
(Thread.currentThread().getName()+":begin...");
//3.阻塞的1000个线程并发执行
Singleton sba = Singleton.getInstance();
System.out.println(Thread.currentThread().getName()+":OK");
//4.释放门栓
last.countDown();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}).start();
}
//2.释放门栓
begin.countDown();
//5.等待1000个线程执行完毕
last.await();
System.out.println("new objects: "+objectcount.get());
}
}