多线程设计模式——二十二、Latch设计模式

Latch(阀门)设计模式也叫做 Count Down 设计模式。当若干个线程并发执行完某个特 定的任务,然后等到所有的子任务都执行结束之后再统一汇总。

CountDownLatch 能够使一个线程在等待另外一些线程完成各自工作之后,再继续执 行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后, 计数器的值就会减一。当计数器的值为 0 时,表示所有的线程都已经完成了任务,然后在 CountDownLatch 上等待的线程就可以恢复执行任务。示例代码如下:

代码1:封装自己的CountDownLatch

package com.bjsxt.chapter22;


public class CustomCountDownLatch {
    private volatile int count;
    public CustomCountDownLatch(int count){
        this.count=count;
    }
    public void countDown(){
        synchronized (this){
            this.count--;
            System.out.println(Thread.currentThread().getName()+" will end.");
            this.notifyAll();// 只会唤醒 main线程
        }
    }
    public void await(){
        synchronized (this){
            try{
                while(count!=0){
                    System.out.println(Thread.currentThread().getName()+" will wait.");// 被唤醒了5次
                    this.wait();
                    System.out.println(Thread.currentThread().getName()+" will go on.");
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}

代码2:外部调用的测试类

package com.bjsxt.chapter22;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) {
        // main ,t1,t2,...t5 6个线程共享资源 count,countDoenLatch
        int count=5;
        final CustomCountDownLatch countDownLatch=new CustomCountDownLatch(count);
        for(int i=1;i<=count;i++){
            new Thread(()->{
                try {
                    // 它没有占用锁资源
                    TimeUnit.SECONDS.sleep(ThreadLocalRandom.current().nextInt(30));// 模拟线程执行时间 0~9 s
                    // t1,t2,...,t5 线程执行完毕后countDownLatch.count数量减一
                    // countDown 占用锁资源 countDownLatch
                    countDownLatch.countDown();
                }catch (Exception e){
                    e.printStackTrace();
                }
            },"t"+i).start();
        }
        // main 线程查询countDownLatch.count 为 0 就启动,不为 0 就进入wait队列的阻塞状态,等待被唤醒
        countDownLatch.await();
        // do sth
        System.out.println("元宵节快乐吖");
    }
}

运行结果:
多线程设计模式——二十二、Latch设计模式
分析:
开始的时候写的是if语句,不是while循环。一个线程执行完毕后,main线程就会输出结果。如下图。改成while循环就好了。
根本原因是t1,至t5处于线程普通的阻塞状态,过几秒进入Runnable状态。只有main线程处于wait队列的阻塞状态,被其他线程notify后会进入锁池,只有他会竞争CountDownLatch锁,竞争上就进入Runnable状态。

public void await(){
        synchronized (this){
            try{
            if(count!=0)
                    System.out.println(Thread.currentThread().getName()+" will wait.");// 被唤醒了5次
                    this.wait();
                    System.out.println(Thread.currentThread().getName()+" will go on.");
              }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

结果
多线程设计模式——二十二、Latch设计模式

查看这六个线程某一时间的jstack,验证分析的是否符合实际:t1至t5的阻塞状态只能是普通的阻塞状态。main是wait队列的阻塞状态。
2021-02-26 14:46:20
main
多线程设计模式——二十二、Latch设计模式
t1,至t5
t2线程处于Terminated状态,其他线程陷入普通的阻塞状态。
多线程设计模式——二十二、Latch设计模式
2021-02-26 14:46:22:
main:处于wait队列的阻塞状态
多线程设计模式——二十二、Latch设计模式

t1至t5
t2,t4处于Terminated状态,其他县城处于普通的阻塞状态。
多线程设计模式——二十二、Latch设计模式
2021-02-26 14:46:29:
main:处于wait队列的阻塞状态
多线程设计模式——二十二、Latch设计模式
t1至t5:
t2,t4,t5依次进入了Terminated状态;其他县城处于普通的阻塞状态。
多线程设计模式——二十二、Latch设计模式
2021-02-26 14:46:33:
main:处于wait队列的阻塞状态
多线程设计模式——二十二、Latch设计模式
t1至t5:
t2,t4,t5,t1依次进入了Terminated状态;t3处于普通的阻塞状态
多线程设计模式——二十二、Latch设计模式
控制台的输出信息:导致输出2,4,5,1,3顺序结束。每次进入countDown方法,执行notifyAll,实际上只会唤醒main线程,所以会输出5次
main will wait和mainwill go on
多线程设计模式——二十二、Latch设计模式

上一篇:MySql事物隔离级别


下一篇:阿里云突发性能t5实例CPU基线性能提升至20%~25%