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

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

假设现在有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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

leetcode递归算法题总结

递归本质是找重复的子问题 本章目录 1.汉诺塔2.合并两个有序链表3.反转链表4.两两交换链表中的节点5.Pow(x,n) 1.汉诺塔 汉诺塔 //面试写法 class Solution { public:void hanota(vector<int>& a, vector<int>& b, vector<int>& c) {dfs(a,b…

基于Spring Cloud + Spring Boot的企业电子招标采购系统源码

随着企业的快速发展&#xff0c;招采管理逐渐成为企业运营中的重要环节。为了满足公司对内部招采管理提升的要求&#xff0c;建立一个公平、公开、公正的采购环境至关重要。在这个背景下&#xff0c;我们开发了一款电子招标采购软件&#xff0c;以最大限度地控制采购成本&#…

Python等高线图的绘制(Matplotlib篇-11)

Python等高线图的绘制(Matplotlib篇-11)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ…

Redis(二)

1、redis的持久化 "Redis 如何将数据写入磁盘"&#xff0c;首先要明白的时候&#xff0c;我们使用的redis的数据保存在内存上的&#xff0c;也就是说&#xff0c;只要我们的电脑关机或者重启服务器&#xff0c;那么在内存中的数据就会消失&#xff0c;所以要想持久化…

(一)CarPlay集成开发之概述与环境篇

系列文章目录 第一章 CarPlay集成开发之概述与环境篇 文章目录 系列文章目录概述开发环境依赖项总结 概述 CarPlay是由苹果公司开发的一款集成在iOS系统中&#xff0c;用于运行在已完成对接该系统的汽车中控台&#xff0c;仪表盘上的车载系统&#xff0c;该系统通过USB或者WI…

【多态模板异常处理表达式】

#include <iostream> #include <vector>using namespace std;template <typename T> class SequenceList { private:vector<T> elements;public:// 获取顺序表的长度int length() const {return elements.size();}// 在指定位置插入元素void insertEle…

前端学习笔记 3:Vue 工程

前端学习笔记 3&#xff1a;Vue 工程 上一篇文章介绍了如何在单一 Html 页面中使用 Vue&#xff0c;本文介绍如何从头开始用 Vue 构建一个前端工程项目。 环境准备 Vue 框架代码的创建依赖于 Node.js&#xff0c;因此需要先安装 Node.js。 创建和启动 创建 通过以下命令可…

Python控制程控电源(USB)

文章目录 前言一、环境搭建1.软件安装2.硬件安装二、设置程控电源连接方式三、Python代码四、验证结果五、pyd文件前言 随着智能电动汽车行业的持续发展,汽车电子或嵌入式设备在软硬件的测试中,都会使用程控电源供电,特别是自动化测试、压力测试场景必定使用到程控电源控制…

docker如何配置阿里云镜像加速?

登录阿里云后&#xff0c;我们点击右上角的控制台&#xff0c;控制台中搜索镜像加速服务&#xff0c;然后点击帮助文档的官方镜像加速&#xff1a; 点击容器镜像服务控制台&#xff1a; 在镜像工具里面的镜像加速器中就可以看到&#xff1a; 分别执行即可&#xff1a; 之后我们…

NVMe SSD IO压力导致宕机案例解读-3

最后找到问题的根因&#xff1a; NVME硬盘&#xff08;mdts参数为10&#xff09;的max_hw_sectors_kb设置为4096KB。当进行流式DMA映射时。如果单次请求的数据量过大&#xff0c;超过了128KB&#xff0c;导致无法有效利用IOVA优化机制&#xff0c;进而引发了对iova_rbtree_loc…

Nacos配置回滚

前言 很多时候&#xff0c;我们会配置错一些属性&#xff0c;或者需要回滚某些属性&#xff0c;这时候使用Nacos的回滚功能就很方便了 配置回滚 1、在控制台中&#xff0c;选择左侧导航栏的 “配置管理”&#xff0c;进入历史版本&#xff0c;选择Group和data id&#xff0c…

基于回溯搜索算法优化的Elman神经网络数据预测 - 附代码

基于回溯搜索算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于回溯搜索算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于回溯搜索优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&…

Vue3.4更新 “Slam Dunk“发布!!!

Announcing Vue 3.4 | The Vue Point. vue3.4更新官方文档 在vue2即将结束更新的时候&#xff0c;vue3迎来了一个重要的更新。代号为“&#x1f3c0; Slam Dunk”&#xff0c;即"灌篮高手"。这个版本进行了很多显著的内部改进&#xff0c;最重要的是模版解析的底层逻…

burpsuite模块介绍之extender(扩展)

extender Burp提供了对第三方拓展插件的支持,使用户能够编写自定义插件或从插件商店中安装拓展插件。这些Burp扩展程序可以以多种方式定制Burp的行为,包括修改HTTP请求和响应、自定义UI、添加自定义扫描程序检查以及访问关键的运行时信息,如代理历史记录、目标站点地图和扫…

松松2023年工作汇报

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 今天是2024年1月3号&#xff0c;是我们新年上班的第2天。今天我们的开会内容主要是回顾2023年公司整体发展的情况&#xff1a; 1.人员方面 整个2023年是我们松松公司人员是最稳定的一年&#xff0c;招聘了2位兼职…

架构设计系列9,10

架构设计系列9&#xff1a;前端架构和后端架构的区别 前端架构和后端架构都是软件系统中最关键的架构层&#xff0c;负责处理不同方面的任务和逻辑&#xff0c;两者之间是存在一些区别和联系的&#xff0c;我会从以下几个方面来阐述&#xff1a; 定位和职责 ● 前端架构主要…

三分钟入门基于Visio的流程图绘制操作

Visio是微软旗下的一款流程图及其他框图绘制工具&#xff0c;有着广泛应用&#xff0c;其高效的展示功能和便捷快速的操作也广受认可。今天&#xff0c;我们就以最基本的流程图绘制为例来一起探索一下Visio的基础功能和用法。 声明&#xff1a;这篇教程从实用角度出发&#xf…

第5章 【例题】(部分~)

【例5.1】使用继承的案例 //【例5.1】使用继承的案例 #include <iostream> #include <string> using namespace std; class Person{ //声明基类public:Person(string name1,string id_number,int age1);~Person();void show(); //在基类中定义了成员函数show()pri…

全院级医学影像PACS源码,影像采集传输与存储管理、影像诊断查询与报告管理

全院医学影像PACS源码&#xff0c;数字化影像信息系统源码&#xff0c;带三维影像后处理技术 全院影像设备联网与影像信息数字化存储&#xff0c;建立涵盖全院的PACS/RIS系统&#xff0c;实现从预约、登记、分诊、排队叫号、检查、诊断阅片、报告发布、自助胶片打印等流程化管…

基于深度学习的交通标志图像分类识别系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 本文详细探讨了一基于深度学习的交通标志图像识别系统。采用TensorFlow和Keras框架&#xff0c;利用卷积神经网络&#xff08;CNN&#xff09;进行模型训练和预测&#xff0c;并引入VGG16迁移学习…