多任务并行处理相关面试题

news2024/11/26 5:26:02

我自己面试时被问过两次多任务并行相关的问题:

假设现在有10个任务,要求同时处理,并且必须所有任务全部完成才返回结果

这个面试题的难点是:

  • 既然要同时处理,那么肯定要用多线程。怎么设计多线程同时处理任务呢?
  • 要求返回结果,那么就不能用简单的Thread+Runnable了,这个是无返回结果的
  • 最难的是,这些任务彼此间还有关系:任务全部结束才算完成

下面3个Demo,CountDownLatch的结果处理交给大家自行完成。

FutureTask

/**
 * @author mx
 */
public class FutureTaskDemo {

    private static AtomicInteger count = new AtomicInteger(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 准备10个线程
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        // 收集每个任务的结果
        List<Future<Integer>> resultList = new ArrayList<>();

        long start = System.currentTimeMillis();

        // 并行处理10个任务
        for (int i = 0; i < 10; i++) {
            // 准备任务
            Callable<Integer> task = () -> {
                // 模拟任务耗时 0~4秒
                int seconds = ThreadLocalRandom.current().nextInt(5);
                TimeUnit.SECONDS.sleep(seconds);
                System.out.println("task is completed! cost:" + seconds + "s left: " + count.decrementAndGet());
                // 模拟返回结果
                return 1;
            };
            // 提交任务
            Future<Integer> partResult = executorService.submit(task);
            // 收集结果
            resultList.add(partResult);
        }

        int result = 0;

        // 阻塞获取并累加结果
        for (Future<Integer> future : resultList) {
            result += future.get();
        }

        // 最终全部任务完成,总耗时取决于最耗时的任务时长
        System.out.println("all task is completed! result=" + result + " cost: " + (System.currentTimeMillis() - start) + "ms");
    }
}

结果展示

task is completed! cost:0s left: 9

task is completed! cost:1s left: 8

task is completed! cost:1s left: 7

task is completed! cost:2s left: 6

task is completed! cost:3s left: 4

task is completed! cost:3s left: 5

task is completed! cost:3s left: 3

task is completed! cost:3s left: 1

task is completed! cost:3s left: 2

task is completed! cost:4s left: 0

all task is completed! result=10 cost: 4110ms

我原先还写过另一个复杂版本:

/**
 * @author mx
 */
public class FutureTaskDemo {

    private static AtomicInteger count = new AtomicInteger(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 准备10个线程
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        // 收集任务
        List<Callable<Integer>> taskList = new ArrayList<>();
        // 收集结果
        List<Future<Integer>> resultList = new ArrayList<>();

        long start = System.currentTimeMillis();

        // 先准备10个任务
        for (int i = 0; i < 10; i++) {
            Callable<Integer> task = () -> {
                // 模拟任务耗时 0~4秒
                int seconds = ThreadLocalRandom.current().nextInt(5);
                TimeUnit.SECONDS.sleep(seconds);
                System.out.println("task is completed! cost:" + seconds + "s left: " + count.decrementAndGet());
                // 模拟返回结果
                return 1;
            };
            // 收集任务
            taskList.add(task);
        }

        // 10个任务并行执行
        for (int i = 0; i < 10; i++) {
            // 从任务列表取出任务,丢到线程池执行
            Future<Integer> partResult = executorService.submit(taskList.get(i));
            // 收集异步结果
            resultList.add(partResult);
        }

        // 最终结果,用于累加
        int result = 0;

        // 是否全部结束,否则一直循环等待
        while (notFinished(resultList)) {
            // wait for all task to be completed...
        }

        // 主线程执行到这,肯定全部任务已经结束,所以get()会立即返回结果,不会再阻塞等待
        for (Future<Integer> future : resultList) {
            result += future.get();
        }

        // 最终全部任务完成,总耗时取决于最耗时的任务时长
        System.out.println("all task is completed! result=" + result + " cost: " + (System.currentTimeMillis() - start) + "ms");
    }

    /**
     * 是否全部完成
     *
     * @param list
     * @return
     */
    private static boolean notFinished(List<Future<Integer>> list) {
        for (Future<Integer> future : list) {
            if (!future.isDone()) {
                return true;
            }
        }
        return false;
    }
}

结果展示

task is completed! cost:0s left: 9

task is completed! cost:0s left: 8

task is completed! cost:2s left: 7

task is completed! cost:3s left: 6

task is completed! cost:3s left: 3

task is completed! cost:3s left: 4

task is completed! cost:3s left: 5

task is completed! cost:4s left: 2

task is completed! cost:4s left: 1

task is completed! cost:4s left: 0

all task is completed! result=10 cost: 4051ms

在当前场景下,其实没必要。

有些人可能觉得第一个版本会出现以下问题:

假设总共就2个任务,但是第一个任务耗时3秒,第二个任务耗时1秒。第二个任务的1秒是建立在第一个任务的3秒后,所以总耗时就变成了4秒。

实际上并不会。当future1#get()阻塞获取第一个任务结果的过程中,第二个任务已经完成,所以future2.get()不会再阻塞,而是直接返回结果。

CountDownLatch

CountDownLatch是什么?学名叫闭锁,俗名叫门栓。

/**
 * @author mx
 */
public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        // 1.先看简单demo,了解下CountDownLatch
        mainThreadAndAsyncThread();
        // 2.尝试使用CountDownLatch解决任务并行问题(不处理结果)
//        multiThreadTask();
    }

    /**
     * CountDownLatch简单demo
     *
     * @throws InterruptedException
     */
    private static void mainThreadAndAsyncThread() throws InterruptedException {
        // 准备一个countDownLatch,设置10,相当于给门加了10把锁
        CountDownLatch countDownLatch = new CountDownLatch(10);

        long start = System.currentTimeMillis();

        // 副线程去处理任务,每个任务耗时1秒,每处理完1个任务就解开一把锁
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                countDownAndPrint(countDownLatch, 1);
            }
        }).start();

        // 一夫当关,万夫莫开。只要门上10把锁没有全部解开,任何线程都别想想往下走
        countDownLatch.await();

        System.out.println("all task is completed! cost: " + (System.currentTimeMillis() - start) + "ms");
    }

    /**
     * CountDownLatch应用:演示10个任务并行执行,全部完成后返回结果
     *
     * @throws InterruptedException
     */
    private static void multiThreadTask() throws InterruptedException {
        // 准备一个countDownLatch,设置10,相当于给门加了10把锁
        CountDownLatch countDownLatch = new CountDownLatch(10);

        long start = System.currentTimeMillis();

        // 启动10个线程执行任务
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    // 线程进来执行任务,随机睡0~4秒
                    int seconds = ThreadLocalRandom.current().nextInt(5);
                    TimeUnit.SECONDS.sleep(seconds);
                    // 每完成一个任务,解开一把锁
                    countDownAndPrint(countDownLatch, seconds);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }

        // 一夫当关,万夫莫开。只要门上10把锁没有全部解开,任何线程都别想想往下走
        countDownLatch.await();

        System.out.println("all task is completed! cost: " + (System.currentTimeMillis() - start) + "ms");
    }

    /**
     * countDown()并且打印,其实是不需要加synchronized的,这里只是为了多线程环境下正确打印
     *
     * @param countDownLatch
     * @param seconds
     */
    private static synchronized void countDownAndPrint(CountDownLatch countDownLatch, int seconds) {
        countDownLatch.countDown();
        System.out.println("task completed, cost: " + seconds + "s left: " + countDownLatch.getCount());
    }
}

结果展示

task is completed! cost:0s left: 9

task is completed! cost:0s left: 8

task is completed! cost:0s left: 7

task is completed! cost:2s left: 6

task is completed! cost:2s left: 5

task is completed! cost:2s left: 3

task is completed! cost:2s left: 4

task is completed! cost:3s left: 2

task is completed! cost:4s left: 1

task is completed! cost:4s left: 0

all task is completed! result=10 cost: 4049ms

CompletableFuture

顺便说一句,上面的两个Demo都是闹着玩呢,实际开发别傻不拉几地自己写哈...会被打,而且是背身单打颜扣的那种。

/**
 * @author mx
 */
public class CompletableFutureDemo {

    private static AtomicInteger count = new AtomicInteger(10);

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        // 准备10个线程
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        // 收集结果
        List<CompletableFuture<Integer>> resultList = new ArrayList<>();

        long start = System.currentTimeMillis();

        // 任务并行
        for (int i = 0; i < 10; i++) {
            CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
                // 模拟任务耗时 0~4秒
                try {
                    int seconds = ThreadLocalRandom.current().nextInt(5);
                    TimeUnit.SECONDS.sleep(seconds);
                    System.out.println("task is completed! cost:" + seconds + "s left: " + count.decrementAndGet());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 模拟返回结果
                return 1;
            }, executorService);
            resultList.add(completableFuture);
        }

        // 处理结果
        int result = 0;
        for (CompletableFuture<Integer> completableFuture : resultList) {
            result += completableFuture.get();
        }

        // 最终全部任务完成,总耗时取决于最耗时的任务时长
        System.out.println("all task is completed! result=" + result + " cost: " + (System.currentTimeMillis() - start) + "ms");
    }
}

结果展示

task is completed! cost:0s left: 9

task is completed! cost:0s left: 8

task is completed! cost:0s left: 7

task is completed! cost:0s left: 6

task is completed! cost:2s left: 5

task is completed! cost:3s left: 4

task is completed! cost:3s left: 3

task is completed! cost:3s left: 2

task is completed! cost:4s left: 1

task is completed! cost:4s left: 0

all task is completed! result=10 cost: 4051ms

实际开发案例

public class CompletableFutureDemo {

    private static AtomicInteger count = new AtomicInteger(2);

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        // 准备10个线程
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        long start = System.currentTimeMillis();

        // 模拟处理订单
        CompletableFuture<Void> dealOrder = CompletableFuture.runAsync(() -> {
            // 模拟任务耗时 0~4秒
            try {
                int seconds = ThreadLocalRandom.current().nextInt(5);
                TimeUnit.SECONDS.sleep(seconds);
                System.out.println("task is completed! cost:" + seconds + "s left: " + count.decrementAndGet());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, executorService);

        // 模拟处理库存
        CompletableFuture<Void> dealStock = CompletableFuture.runAsync(() -> {
            // 模拟任务耗时 0~4秒
            try {
                int seconds = ThreadLocalRandom.current().nextInt(5);
                TimeUnit.SECONDS.sleep(seconds);
                System.out.println("task is completed! cost:" + seconds + "s left: " + count.decrementAndGet());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, executorService);

        // 可变参数,可以传任意个CompletableFuture,阻塞等待所有任务完成
        CompletableFuture.allOf(dealOrder, dealStock).get();

        // 最终全部任务完成,总耗时取决于最耗时的任务时长
        System.out.println("all task is completed! cost: " + (System.currentTimeMillis() - start) + "ms");
    }
}

结果展示

task is completed! cost:2s left: 1

task is completed! cost:3s left: 0

all task is completed! cost: 3058ms

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1349828.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】—— 匿名管道

前言&#xff1a; 接下来我将带大家探索 进程间通信 的方式。本期&#xff0c;要讲的就是管道其中之一“匿名管道”&#xff01;&#xff01; 目录 &#xff08;一&#xff09;进程间通信介绍 1、进程间通信目的 2、进程间通信发展 3、进程间通信分类 &#xff08;二&…

2024年01月编程语言流行度排名

点击查看最新编程语言流行度排名&#xff08;每月更新&#xff09; 2024年01月编程语言流行度排名 编程语言流行度排名是通过分析在谷歌上搜索语言教程的频率而创建的 一门语言教程被搜索的次数越多&#xff0c;大家就会认为该语言越受欢迎。这是一个领先指标。原始数据来自…

linux休眠机制介绍

一、概述 Linux系统提供了休眠和低功耗模式&#xff0c;可以帮助节省电力和延长电池寿命&#xff0c;休眠对应的另外一种模式就是唤醒。 二、常用的休眠方式 常用的休眠方式有freeze,standby, mem, disk&#xff0c;hibernate freeze: 冻结所有的进程&#xff0c;包括用户空…

微信小程序-页面开发

文章目录 微信小程序第二章2. 页面开发2.1 创建开发页面2.2 修改项目首页2.3 页面的结构和样式设计2.3.1 WXML结构设计2.3.1.1 什么是WXML2.3.1.2 WXML的常见标签2.3.1.3 WXML的特点 2.3.2 WXSS样式设计2.3.2.1 什么是WXSS 2.4 组件库的使用和自定义组件2.4.1 小程序中的组件分…

基于 Haar 特征的人脸检测

Haar分类器概述 Haar分类器采用的是Viola-Jones人脸检测算法 该算法需要用到大量的积极图片&#xff08;包含人脸的图片&#xff09;和消极图片&#xff08;不包含人脸的图片&#xff09;&#xff0c;从中提取类Haar特征( Haar-like features)&#xff0c;经过反复训练&#…

TF-IDF(Term Frequency-Inverse Document Frequency)算法 简介

TF-IDF&#xff08;Term Frequency-Inverse Document Frequency&#xff09;是一种用于信息检索和文本挖掘的常用算法。它用于评估一个词对于一个文档集合中某个文档的重要性。 这个算法的基本思想是&#xff1a;如果一个词在一个文档中频繁出现&#xff0c;并且在整个文档集合…

MySQL基础笔记(1)基础理论

一.基本概念 DB&#xff1a;数据库&#xff0c;存储数据的仓库&#xff0c;数据是有组织地进行存储DBMS&#xff1a;操纵和管理数据库的大型软件 SQL&#xff1a;结构化查询语言&#xff0c;操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库的统一标准 &…

CSDN规则详解(二)

文章目录 每日一句正能量前言博文发布说明创建专栏显示数量上限问题私信规则说明会员下载次数说明后记 每日一句正能量 与其战胜敌人一万次&#xff0c;不如战胜自己一次。 前言 在数字技术的浪潮下&#xff0c;中国的开发者社区蓬勃发展&#xff0c;而CSDN则是其中的佼佼者。…

[蓝桥杯知识学习] 树链

DFS序 什么是DFS序 怎么求DFS序 进入操作&#xff0c;将有计数 出&#xff1a;可以理解为&#xff0c;没有孩子可以去了&#xff08;不能&#xff0c;向下行动&#xff1a;对应于程序里的入栈&#xff09;&#xff0c;所以回到父结点&#xff08;向上行动&#xff0c;对应于程…

我在CSDN的2023年

一、引言 在2023年的这一年当中&#xff0c;在CSDN的生活让我得到许多知识与启发&#xff0c;也让我获得一些快乐和成就 二、自己的收获 在这一年当中&#xff0c;我从一个只会看别人写的文章解决问题到&#xff0c;可以自己写文章帮别人解决问题&#xff0c;这种成就感是极大…

JavaScript history对象常用方法【通俗易懂】

✨前言✨   本篇文章主要在于了解及使用JavaScript中history对象常用方法 &#x1f352;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f352;博主将持续更新学习记录收获&#xff0c;友友们有任何问题可以在评论区留言 文章目录 一&…

【熔断限流组件resilience4j和hystrix】

文章目录 &#x1f50a;博主介绍&#x1f964;本文内容起因resilience4j落地实现pom.xml依赖application.yml配置接口使用 hystrix 落地实现pom.xml依赖启动类上添加注解接口上使用 &#x1f4e2;文章总结&#x1f4e5;博主目标 &#x1f50a;博主介绍 &#x1f31f;我是廖志伟…

thinkphp6入门(14)-- 多关联模型查询

背景&#xff1a; 有3个数据表&#xff0c;一个User表&#xff0c;一个Cloth表&#xff0c;一个Shoe表。 Cloth表和Shoe表分别和User表通过user_id关联。 thinkphp 6中如何通过模型查询所有用户&#xff0c;其中包括每个用户的cloth和shoe。 多关联模型查询&#xff1a; 1.…

热塑性塑料激光透光率检测仪

热塑性塑料是一类在一定温度下具有可塑性&#xff0c;冷却后固化且能重复这种过程的塑料。它们通常由线型高分子化合物组成&#xff0c;这些分子在受热时不会发生交联。热塑性塑料可以通过回收和再加工&#xff0c;重新制成新的产品。根据其性能特点、用途广泛性和成型技术通用…

YOLOv8改进 | 2023主干篇 | FasterNeT跑起来的主干网络( 提高FPS和检测效率)

一、本文介绍 本文给大家带来的改进机制是FasterNet网络&#xff0c;将其用来替换我们的特征提取网络&#xff0c;其旨在提高计算速度而不牺牲准确性&#xff0c;特别是在视觉任务中。它通过一种称为部分卷积&#xff08;PConv&#xff09;的新技术来减少冗余计算和内存访问。…

漏洞复现-海康威视网络对讲广播系统远程命令执行漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

策略模式示例,Lambda表达式

这样会有很多代码冗余 以上代码使用策略模式优化 优化方案1 那么现在可以这样 优化方案二 原先定义了接口,还需要一个个写实现类,其实完全没必要,用匿名内部类方式就可以 优化方案三:用lambda方式简写 优化方案四:不需要定义接口 使用Stream API

JavaScript中alert、prompt 和 confirm区别及使用【通俗易懂】

✨前言✨   本篇文章主要在于&#xff0c;让我们看几个与用户交互的函数&#xff1a;alert&#xff0c;prompt 和confirm的使用及区别 &#x1f352;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f352;博主将持续更新学习记录收获&…

【Shell编程练习】猜大小

系列文章目录 输出Hello World 通过位置变量创建 Linux 系统账户及密码 监控内存和磁盘容量&#xff0c;小于给定值时报警 系列文章目录脚本生成一个 100 以内的随机数,提示用户猜数字&#xff0c;根据用户的输入&#xff0c;提示用户猜对了&#xff0c;猜小了或猜大了&#x…

CMake入门教程【基础篇】CMake是什么?为什么学习CMake

文章目录 1.CMake简介2.为什么要学习CMake3.什么样的项目需要用CMake3.1大型或复杂项目3.2跨平台项目3.3需要高度定制化构建的项目3.4 研究和开源项目3.5小型或简单项目 4.CMake的作用5.支持的编译器5.1Windows平台5.2Unix/Linux平台5.3macOS平台5.4其他编译器5.5支持的平台 CM…