# 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()); } }