java多线程系列:CountDownLatch

简介:

这篇文章将介绍CountDownLatch这个同步工具类的基本信息以及通过案例来介绍如何使用这个工具。

CountDownLatch是java.util.concurrent包下面的一个工具类,可以用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。 它可以允许一个或者多个线程等待其他线程完成操作。
图片来源于网络

案例

模拟游戏一开始需要加载一些基础数据后才能开始游戏,基础数据加载完可以继续加载其他数据。基础数据包含人物、地图、背景、物品等等。

解决方案

利用CountDownLatch来实现,基础数据加载完毕后,CountDownLatch计数器进行减一操作。当CountDownLatch计数器为0时,表示可以开始游戏。 示意图如下

定义抽象类

定义抽象类AbstractDataRunnable并实现Runnable接口

抽象类包含两个属性

private String name;
private CountDownLatch count;

通过构造函数初始化两个属性

public AbstractDataRunnable(String name, CountDownLatch count) {
    this.name = name;
    this.count = count;
}

定义方法,提供一个抽象方法handle()供子类去实现,getName()afterCountDown()提供默认的实现。

public String getName() {
    return name;
}

public abstract void handle() throws InterruptedException;

public void afterCountDown(){
    System.out.println(this.getName() + ":CountDownLatch计数减一之后,继续加载其他数据...");
};

run方法如下,在调用handle()方法之后执行count.countDown();,让CountDownLatch计数器进行减一操作.计数器减一之后可以继续加载额外的数据,并不影响当前线程

public void run() {
    try {
        System.out.println(this.getName()+" 开始加载...");
        Long l1 = System.currentTimeMillis();
        handle();
        Long l2 = System.currentTimeMillis();
        System.out.println(this.getName()+" 加载完成,花费时间:"+(l2-l1));
    } catch (Exception e){
        e.printStackTrace();
    } finally {
        count.countDown();
    }
    afterCountDown();
}

定义一些数据加载类

背景数据加载类如下,实现了抽象类AbstractDataRunnablehandle()方法,在handle()方法休眠了2秒

public class BackGroundData extends AbstractDataRunnable {

    public BackGroundData(String name, CountDownLatch count) {
        super(name, count);
    }

    @Override
    public void handle() throws InterruptedException {
        //模拟加载时间,2秒
        Thread.sleep(2000);
    }
}

其他数据加载类代码就不贴出来了,睡眠的时间不同而已

开始游戏

开始游戏类如下,通过构造函数传入CountDownLatch计数器,然后在run方法中执行count.await();方法进行等待基础数据加载完毕。

class StartGame implements Runnable{
    private CountDownLatch count;

    public StartGame(CountDownLatch count) {
        this.count = count;
    }

    @Override
    public void run() {
        try {
            System.out.println("开始加载基础数据...");
            Long l1 = System.currentTimeMillis();
            count.await();
            Long l2 = System.currentTimeMillis();
            System.out.println("基础数据加载完毕,总共花费时长:"+(l2-l1)+".可以开始游戏...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试

public static void main(String[] args) throws IOException {
    CountDownLatch count = new CountDownLatch(4);

    //主线程
    Thread startGameThread = new Thread(new StartGame(count));
    startGameThread.start();

    //加载数据线程
    Thread mapThread = new Thread(new MapData("地图",count));
    Thread goodsThread = new Thread(new GoodsData("物品",count));
    Thread personageThread = new Thread(new PersonageData("人物",count));
    Thread backGroundThread = new Thread(new BackGroundData("背景",count));


    mapThread.start();
    goodsThread.start();
    personageThread.start();
    backGroundThread.start();

    System.in.read();
}

测试结果内容

开始加载基础数据...
地图 开始加载...
物品 开始加载...
人物 开始加载...
背景 开始加载...
人物 加载完成,花费时间:1000
人物:CountDownLatch计数减一之后,继续加载其他数据...
背景 加载完成,花费时间:2000
背景:CountDownLatch计数减一之后,继续加载其他数据...
物品 加载完成,花费时间:2501
物品:CountDownLatch计数减一之后,继续加载其他数据...
地图 加载完成,花费时间:3001
地图:CountDownLatch计数减一之后,继续加载其他数据...
基础数据加载完毕,总共花费时长:3003.可以开始游戏...

案例源代码地址:https://github.com/rainbowda/learnWay/tree/master/learnConcurrency/src/main/java/com/learnConcurrency/utils/countDownLatch

有兴趣的点个Star

目录
相关文章
|
4天前
|
安全 Java UED
Java中的多线程编程:从基础到实践
本文深入探讨了Java中的多线程编程,包括线程的创建、生命周期管理以及同步机制。通过实例展示了如何使用Thread类和Runnable接口来创建线程,讨论了线程安全问题及解决策略,如使用synchronized关键字和ReentrantLock类。文章还涵盖了线程间通信的方式,包括wait()、notify()和notifyAll()方法,以及如何避免死锁。此外,还介绍了高级并发工具如CountDownLatch和CyclicBarrier的使用方法。通过综合运用这些技术,可以有效提高多线程程序的性能和可靠性。
|
3天前
|
缓存 Java UED
Java中的多线程编程:从基础到实践
【10月更文挑战第13天】 Java作为一门跨平台的编程语言,其强大的多线程能力一直是其核心优势之一。本文将从最基础的概念讲起,逐步深入探讨Java多线程的实现方式及其应用场景,通过实例讲解帮助读者更好地理解和应用这一技术。
19 3
|
5天前
|
缓存 安全 Java
使用 Java 内存模型解决多线程中的数据竞争问题
【10月更文挑战第11天】在 Java 多线程编程中,数据竞争是一个常见问题。通过使用 `synchronized` 关键字、`volatile` 关键字、原子类、显式锁、避免共享可变数据、合理设计数据结构、遵循线程安全原则和使用线程池等方法,可以有效解决数据竞争问题,确保程序的正确性和稳定性。
13 2
|
7天前
|
存储 安全 Java
Java-如何保证线程安全?
【10月更文挑战第10天】
|
7天前
|
Java
|
8天前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
21 1
|
2天前
|
缓存 算法 Java
|
7天前
|
Java 应用服务中间件 测试技术
Java21虚拟线程:我的锁去哪儿了?
【10月更文挑战第8天】
19 0
|
13天前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
31 1
C++ 多线程之初识多线程
|
13天前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
36 6