11.countDownLatch(共享锁)

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

参考:《CountDownLatch 压测教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值