Java并发之CountDownLatch的基本使用

前言

    利用CountDownLatch进行线程间同步,可以控制其线程的执行时序,谁先执行、谁后执行,利用好CountDownLatch即可

举例

    需求:共计3个线程参与,1个主线程、2个工作线程,主线程等待另外2个工作线程完成任务后,主线程再继续执行,这就是线程间的同步

package com.wp.blockthread;

import java.util.Date;
import java.util.concurrent.CountDownLatch;

public class CountDownDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch=new CountDownLatch(2);  //主线程中创建CountDownLatch对象,计数器为2
        DressingThread firstGirl=new DressingThread("Alisa", 5000, latch);  //一号工作线程,持有同一个CountDownLatch对象
        DressingThread secondGirl=new DressingThread("Lily", 4000, latch);  //二号工作线程,持有同一个CountDownLatch对象
        firstGirl.start(); 
        secondGirl.start(); 
        latch.await(); //主线程阻塞在此行,当CountDownLatch对象的计数为0时,主线程才能继续执行
        System.out.println("All girls dressing done at "+ new Date(System.currentTimeMillis()));
    }


    static class DressingThread extends Thread{
        String girlName;
        int dressingDuration;
        CountDownLatch latch; //当前线程对象负责持有CountDownLatch对象
        public Dressing(String girlName , int duration, CountDownLatch latch){
            this.girlName =girlName;
            this.dressingDuration = duration;
            this.latch=latch;
        }
        public void run(){
            System.out.println(girlName +" do dressing begin at "+ getCurrentTime());
            doWork();
            System.out.println(girlName +" do dressing complete at "+ getCurrentTime());
            latch.countDown(); //工作线程调用此方法时,同一个CountDownLatch对象的计数器会减少1
        }

        private void doWork(){
            try {
                Thread.sleep(dressingDuration); //线程休眠,模拟工作线程执行任务
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        private Date getCurrentTime() {
            return new Date(System.currentTimeMillis());
        }
    }


}

使用过程分析

1、首先在主线程中创建CountDownLatch对象,并传入一个计数器值:2,此时工作线程还未创建

        CountDownLatch latch=new CountDownLatch(2);  //主线程中创建CountDownLatch对象,计数器为2

2、接着在主线程再创建两个Work Thread对象,同时将同一个CountDownLatch对象的引用传递进去,此时每个Work Thread对象持有的是同一个CountDownLatch对象

        DressingThread firstGirl=new DressingThread("Alisa", 5000, latch);  //一号工作线程,持有同一个CountDownLatch对象
        DressingThread secondGirl=new DressingThread("Lily", 4000, latch);  //二号工作线程,持有同一个CountDownLatch对象

3、继续在主线程中开始启动2个工作线程,此时工作线程才开始执行

        firstGirl.start(); 
        secondGirl.start(); 

4、启动完工作线程后,主线程主动调用CountDownLatch对象的await()方法,由于CountDownLatch对象的计数器不为0,该方法会使当前主线程进入阻塞状态,这样就可以等待工作线程的执行了

        latch.await(); //主线程阻塞在此行,只有当CountDownLatch对象的计数为0时,主线程才能继续执行

5、每个工作线程的run方法中,会执行业务逻辑,当工作线程中的业务逻辑执行结束,会调用持有的同一个CountDownLatch对象的countDown()方法,每次调用countDown()方法都会将CountDownLatch对象持有的计数器值减去1

        public void run(){
            System.out.println(girlName +" do dressing begin at "+ getCurrentTime());
            doWork();
            System.out.println(girlName +" do dressing complete at "+ getCurrentTime());
            latch.countDown(); //工作线程调用此方法时,同一个CountDownLatch对象的计数器会减少1
        }

6、当CountDownLatch对象持有的计数器等于0时,阻塞的主线程会被唤醒,然后从阻塞代码处继续运行,我们有两个工作线程,都会调用同一个CountDownLatch对象的countDown()方法,所以这个计数器值会成为0

        System.out.println("All girls dressing done at "+ new Date(System.currentTimeMillis()));

输出结果

Lily do dressing begin at Thu Jan 17 21:40:35 CST 2019
Alisa do dressing begin at Thu Jan 17 21:40:35 CST 2019
Lily do dressing complete at Thu Jan 17 21:40:39 CST 2019
Alisa do dressing complete at Thu Jan 17 21:40:40 CST 2019
all dressing done at Thu Jan 17 21:40:40 CST 2019

总结

1、CountDownLatch类的内部维护一个初始值,该值称为计数器,主线程执行await()方法时,如果计数器值大于0时,主线程将会被阻塞,当另外的线程完成任务后,调用CountDownLatch对象的coutDown()方法时,计数器值就会减1

2、当这个计数器值等于0时,处于阻塞等待的线程将会被唤醒,则可以继续执行程序了!

3、线程的组合随你搭配,CountDownLatch是线程间同步用的工具类,相当的nice!

4、有没有发现一个风险,如果工作线程因为异常结束,而没有调用CountDownLatch的coutDown()方法,将会导致主线程一直阻塞在await()方法处,你知道怎么规避这种现象吗???卧槽这是个坑啊,所以我们尽量要使用带有timeout时间的等待,而不是死等。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值