zoukankan      html  css  js  c++  java
  • 【Java&Go并发编程系列】4.等待一组并发任务完成——CountDownLatch VS sync.WaitGroup

    说明:Java & Go 并发编程序列的文章,根据每篇文章的主题或细分标题,分别演示 Java 和 Go 语言当中的相关实现。更多该系列文章请查看:Java & Go 并发编程系列

    本文介绍 Java 和 Go 语言中如何实现并发编程中等待一组并发任务完成的场景。

    代码场景:假设有一道题目,同时分发给10个同学来完成,所有同学完成之后,老师公布答案。

    「Java」CountDownLatch

    使用 CountDownLatch 等待一组并发任务的完成,包含如下三要素:

    • 一个初始值,即定义需要等待的任务的数目
    • await() 需要等待并发任务先完成的线程调用
    • countDown(),每个任务在完成的时候调用
    public static void main(String[] args) throws InterruptedException {
    
        // 声明初始计数为10
        CountDownLatch countDownLatch = new CountDownLatch(10);
    
        // 起10个线程模拟10个同学做题目
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(() -> {
                // 生成从1-10的随机整数,模拟做题目花费的时间
                int randomSecond = ThreadLocalRandom.current().nextInt(10) + 1;
                try {
                    TimeUnit.SECONDS.sleep(randomSecond);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.printf("第%s号同学用时%ds完成了题目
    ", Thread.currentThread().getName(), randomSecond);
                // 计数减1
                countDownLatch.countDown();
            });
            // 设置线程名称,用于打印对应的同学编号
            t.setName(String.valueOf(i));
            t.start();
        }
    
        // 阻塞直到 CountDownLatch 的计数为0
        // 注意这里是 await 方法, 不是 wait 方法
        countDownLatch.await();
        System.out.printf("所有的同学都完成了题目,老师公布答案
    ");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    输出结果:

    第2号同学用时2s完成了题目
    第9号同学用时2s完成了题目
    第8号同学用时2s完成了题目
    第3号同学用时2s完成了题目
    第5号同学用时4s完成了题目
    第6号同学用时6s完成了题目
    第1号同学用时8s完成了题目
    第0号同学用时9s完成了题目
    第4号同学用时9s完成了题目
    第7号同学用时9s完成了题目
    所有的同学都完成了题目,老师公布答案
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    「Go」sync.WaitGroup

    使用 sync.WaitGroup 等待一组并发任务的完成,包含如下三要素:

    • Add(),传入一个 int 类型的参数,增加计数器的数值
    • Wait(),需要等待并发任务先完成的 goroutine 调用
    • Done(),每个任务在完成的时候调用
    func main() {
    	// sync.WaitGroup 是开箱即用的类型
    	var wg sync.WaitGroup
    	// 初始化计数器的值为10
    	wg.Add(10)
    	// 启用10个 goroutine 用于并发执行 go 函数 
    	// goroutine,即用户级线程,也称协程,是 Go 语言并发编程模型中重要的三个元素 GPM 之一
    	for i := 0; i < 10; i++ {
    		go func(i int) {
    			randomSecond := rand.Intn(10) + 1
    			time.Sleep(time.Second * time.Duration(randomSecond))
    			fmt.Printf("第%d号同学用时%ds完成了题目
    ", i, randomSecond)
    			// 计数器减1
    			wg.Done()
    		}(i)
    	}
    	// 阻塞直到所有并发任务执行完成
    	wg.Wait()
    	fmt.Printf("所有的同学都完成了题目,老师公布答案
    ")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    为了让程序的逻辑更严密,可以使用 defer 语句来保证 wg.Done() 一定被执行。

    func main() {
    	var wg sync.WaitGroup
    	wg.Add(10)
    	for i := 0; i < 10; i++ {
    		go func(i int) {
    			// defer 语句用于延迟执行代码,在函数正常或异常结束的时刻执行
    			// 类似于 Java 的 finally
    			defer wg.Done()
    			randomSecond := rand.Intn(10) + 1
    			time.Sleep(time.Second * time.Duration(randomSecond))
    			fmt.Printf("第%d号同学用时%ds完成了题目
    ", i, randomSecond)
    		}(i)
    	}
    	wg.Wait()
    	fmt.Printf("所有的同学都完成了题目,老师公布答案
    ")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    输出结果:

    第2号同学用时1s完成了题目
    第0号同学用时1s完成了题目
    第6号同学用时2s完成了题目
    第1号同学用时2s完成了题目
    第8号同学用时6s完成了题目
    第4号同学用时7s完成了题目
    第5号同学用时8s完成了题目
    第9号同学用时8s完成了题目
    第7号同学用时9s完成了题目
    第3号同学用时10s完成了题目
    所有的同学都完成了题目,老师公布答案
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    拓展

    「Java」CyclicBarrier

    CyclicBarrier 可传入另一个 Runnable 对象,在所有线程都到达集合点后,将运行该 Runnable 对象,使用场景更广泛。

    public static void main(String[] args) {
    
        // 声明10参与者和结束时要运行的 Runnable 对象
        // 这里只是打印了一行文字
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10,
                () -> System.out.printf("所有的同学都完成了题目,老师公布答案
    ")
        );
    
        // 起10个线程模拟10个同学做题目
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(() -> {
                // 生成从1-10的随机整数,模拟做题目花费的时间
                int randomSecond = ThreadLocalRandom.current().nextInt(10) + 1;
                try {
                    TimeUnit.SECONDS.sleep(randomSecond);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.printf("第%s号同学用时%ds完成了题目
    ", Thread.currentThread().getName(), randomSecond);
                try {
                    // 阻塞直到所有参与者都调用 await 方法
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
            // 设置线程名称
            t.setName(String.valueOf(i));
            t.start();
        }
    
        System.out.printf("主线程执行结束
    ");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    输出结果:

    主线程执行结束
    第1号同学用时1s完成了题目
    第5号同学用时1s完成了题目
    第0号同学用时2s完成了题目
    第3号同学用时4s完成了题目
    第9号同学用时4s完成了题目
    第2号同学用时5s完成了题目
    第6号同学用时5s完成了题目
    第4号同学用时6s完成了题目
    第7号同学用时8s完成了题目
    第8号同学用时10s完成了题目
    所有的同学都完成了题目,老师公布答案
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    更多该系列文章请查看:Java & Go 并发编程系列

  • 相关阅读:
    List of the best open source software applications
    Owin对Asp.net Web的扩展
    NSwag给api加上说明
    'workspace' in VS Code
    unable to find valid certification path to requested target
    JMeter的下载以及安装使用
    exception disappear when forgot to await an async method
    Filter execute order in asp.net web api
    记录web api的request以及response(即写log)
    asp.net web api的源码
  • 原文地址:https://www.cnblogs.com/heihei1990/p/13936911.html
Copyright © 2011-2022 走看看