JUC中等待多线程完成的工具类CountDownLatch

CountDownLatch介绍

CountDownLatch称之为闭锁,它可以使一个或一批线程在闭锁上等待,等到其他线程执行完相应操作后,闭锁打开,这些等待的线程才可以继续执行。确切的说,闭锁在内部维护了一个倒计数器。通过该计数器的值来决定闭锁的状态,从而决定是否允许等待的线程继续执行。
常用方法

public CountDownLatch(int count):构造方法,count表示计数器的值,不能小于0,否者会报异常。

**public void await() throws InterruptedException:**调用await()会让当前线程等待,直到计数器为0的时候,方法才会返回,此方法会响应线程中断操作。

public boolean await(long timeout, TimeUnit unit) throws InterruptedException:限时等待,在超时之前,计数器变为了0,方法返回true,否者直到超时,返回false,此方法会响应线程中断操作。

**public void countDown():**让计数器减1

CountDownLatch使用步骤:

1.创建CountDownLatch对象
2.调用其实例方法 await(),让当前线程等待
3.调用 countDown()方法,让计数器减1
4.当计数器变为0的时候, await()方法会返回

举例:有3个人参见跑步比赛,需要先等指令员发指令枪后才能开跑,所有人都跑完之后,指令员喊一声,大家跑完了

package com.example.demo.demo.CountDownLatchDemo;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 代码中,t1、t2、t3启动之后,都阻塞在 commanderCd.await();,
 * 主线程模拟发枪准备操作耗时5秒,然后调用 commanderCd.countDown();
 * 模拟发枪操作,此方法被调用以后,阻塞在 commanderCd.await();的3个线程会向下执行。
 * 主线程调用 countDownLatch.await();之后进行等待,每个人跑完之后,调用 countDown.countDown();
 * 通知一下 countDownLatch让计数器减1,最后3个人都跑完了,主线程从 countDownLatch.await();返回继续向下执行
 */
public class CountDownLatchTest {
    public static class T extends  Thread{
        //每个线程跑步耗费的时间
        int runCostTime;
        CountDownLatch commanderCd;
        CountDownLatch countDownLatch;

        public T(String name, int runCostTime, CountDownLatch commanderCd,CountDownLatch countDownLatch) {
            super(name);
            this.runCostTime = runCostTime;
            this.commanderCd = commanderCd;
            this.countDownLatch=countDownLatch;
        }

        @Override
        public void run() {
            //准备跑步,等待发枪信号
            try {
                commanderCd.await();

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
//            开始时间
            long startTime = System.currentTimeMillis();
            System.out.println(startTime+","+Thread.currentThread().getName()+"开始跑!");
            try {
                //跑步消耗的时间
                TimeUnit.SECONDS.sleep(this.runCostTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            finally {
                countDownLatch.countDown();
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime+","+Thread.currentThread().getName()+"跑步结束,耗时:"+(endTime-startTime));
        }
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis()+","+Thread.currentThread()+"启动!");
        CountDownLatch commanderCd = new CountDownLatch(1);
        CountDownLatch countDownLatch = new CountDownLatch(3);
        long startTime = System.currentTimeMillis();//比赛开始时间
        T t1= new T("小张",2,commanderCd,countDownLatch);
        t1.start();
        T t2= new T("小红",5,commanderCd,countDownLatch);
        t2.start();
        T t3= new T("小黄",7,commanderCd,countDownLatch);
        t3.start();
        //线程在队列中等待,让主线程等待,准备发枪,其余线程进入就绪
        TimeUnit.SECONDS.sleep(5);
        System.out.println(System.currentTimeMillis()+",枪响了,开始跑");
        commanderCd.countDown();//赛道上比赛开始跑起来
        countDownLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println(System.currentTimeMillis()+","+Thread.currentThread().getName()+"结束,耗时"+(endTime-startTime));
    }
}

1591104244695,Thread[main,5,main]启动!
1591104249699,枪响了,开始跑
1591104249699,小张开始跑!
1591104249699,小红开始跑!
1591104249699,小黄开始跑!
1591104251700,小张跑步结束,耗时:2001
1591104254699,小红跑步结束,耗时:5000
1591104256700,小黄跑步结束,耗时:7001
1591104256700,main结束,耗时12003

手写一个并行处理任务的工具类

package com.example.demo.demo.CountDownLatchDemo;

import org.springframework.util.CollectionUtils;

import java.sql.Time;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 并行处理任务的工具类
 */
public class TaskDisposeUtils {
    //并发线程数
    public static  final int Pool_size;
    static {
        Pool_size = Integer.max(Runtime.getRuntime().availableProcessors(),5);
    }
    /**
     * 并行性处理,等待结束
     */
    public static <T> void dispose(List<T> taskList, Consumer<T> consumer) throws InterruptedException{
        dispose(true,Pool_size,taskList,consumer);
    }

    /**
     * 并行处理,并等待结束
     * @param moreThread 是否多线程执行
     * @param pool_size 线程池大小
     * @param taskList 任务列表
     * @param consumer 消费者
     * @param <T>
     */
    public  static <T> void dispose(boolean moreThread, int pool_size, List<T> taskList, Consumer<T> consumer) throws InterruptedException {
        if (CollectionUtils.isEmpty(taskList)){
            return;
        }
        if(moreThread && pool_size>1){//多线程
            pool_size = Math.min(pool_size,taskList.size());
            ExecutorService executorService = null;

            try{
                executorService = Executors.newFixedThreadPool(pool_size);//创建线程池
                CountDownLatch countDownLatch = new CountDownLatch(taskList.size());
                for (T item:taskList) {
                    executorService.execute(() ->{
                        try {
                            consumer.accept(item);
                        }
                        finally {
                            countDownLatch.countDown();
                        }
                    });
                }
                countDownLatch.await();
            }
            finally {
                if(executorService !=null){
                    executorService.shutdown();
                }
            }
        }else {//单线程
            for (T item:taskList) {
                consumer.accept(item);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //生成1-10的数字,放在list中,相当于10个任务
        List<Integer> list = Stream.iterate(1, a -> a + 1).limit(10).collect(Collectors.toList());
        //启动多线程处理list中的数据,每个任务休眠时间为list中的数据
        TaskDisposeUtils.dispose(list,item->{
            try{//实际开发,并行任务处理在try中
                long startTime = System.currentTimeMillis();
                TimeUnit.SECONDS.sleep(item);
                long endTime = System.currentTimeMillis();
                
                System.out.println(System.currentTimeMillis()+".任务"+item+"执行完毕,耗时"+(endTime-startTime));
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        });   
        System.out.println(list+"中的任务处理完毕");
    }

}

1591106690601.任务1执行完毕,耗时1000
1591106691602.任务2执行完毕,耗时2001
1591106692601.任务3执行完毕,耗时3000
1591106693601.任务4执行完毕,耗时4000
1591106694602.任务5执行完毕,耗时5000
1591106695603.任务6执行完毕,耗时6001
1591106696602.任务7执行完毕,耗时7000
1591106697602.任务8执行完毕,耗时8000
1591106698602.任务9执行完毕,耗时9000
1591106699602.任务10执行完毕,耗时10000
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]中的任务处理完毕
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值