首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java避坑指南:多线程批量调用下游接口,如何正确设置总超时时间

Java避坑指南:多线程批量调用下游接口,如何正确设置总超时时间

作者头像
崔认知
发布于 2024-09-13 05:54:51
发布于 2024-09-13 05:54:51
63100
代码可运行
举报
文章被收录于专栏:nobodynobody
运行总次数:0
代码可运行

多线程批量调用下游接口,设置总超时时间是一种常见的需求,特别是在需要保证程序在预定时间内必须返回,否则超时设置不合理,导致接口变慢。

设置场景:多线程批量执行三个接口,耗时分别为10s、15s、20s(一般不会设置这么大的超时时间,此值为了模拟),总超时时间为15s。

错误做法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.renzhikeji.demo;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class JdkDemo {
    private static final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 100, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100), Thread::new,
            new ThreadPoolExecutor.AbortPolicy() {
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                    System.out.println("rejectedExecution");
                    super.rejectedExecution(r, e);
                }
            });

    public static void main(String[] args) {
        List<Future<Integer>> futures = new ArrayList<>(10);
        Future<Integer> future1 = poolExecutor.submit(() -> {
            System.out.println(Thread.currentThread());
            TimeUnit.SECONDS.sleep(10);
            return 1;
        });

        futures.add(future1);
        Future<Integer> future2 = poolExecutor.submit(() -> {
            System.out.println(Thread.currentThread());
            TimeUnit.SECONDS.sleep(15);
            return 1;
        });

        futures.add(future2);
        Future<Integer> future3 = poolExecutor.submit(() -> {
            System.out.println(Thread.currentThread());
            TimeUnit.SECONDS.sleep(20);
            return 1;
        });

        futures.add(future3);


        long start = System.currentTimeMillis();
        for (Future<Integer> integerFuture : futures) {
            try {
                integerFuture.get(15, TimeUnit.SECONDS);
            } catch (Throwable e) {
                e.printStackTrace();
            }

        }

        long d = System.currentTimeMillis() - start;
        System.out.println(d / 1000);

     }

}

执行结果:总超时时间为20s,大于预设置的15s。

上述错误做法:线程池提交任务后,每个任务的超时时间都设置为一个固定值,从而总任务超时超时延长。

java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit)方法是对每个任务的超时时间设置,而不是对总任务设置超时时间。

注意:必须保证所有的任务同时执行,核心线程数必须大于等于3,否则会进入队列等待,超时时间会更长。

线程池实现原理解析 崔认知,公众号:认知科技技术团队【八股文Java】图解Java线程池实现原理(ThreadPoolExecutor)

正确做法:使用线程池invokeAll方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.renzhikeji.demo;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class JdkDemo {
    private static final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), Thread::new, new ThreadPoolExecutor.AbortPolicy() {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            System.out.println("rejectedExecution");
            super.rejectedExecution(r, e);
        }
    });

    public static void main(String[] args) {
        List<Future<Integer>> futures = new ArrayList<>(10);
        Callable<Integer> callable1 = () -> {
            System.out.println(Thread.currentThread());
            TimeUnit.SECONDS.sleep(10);
            return 1;
        };


        Callable<Integer> callable2 = () -> {
            System.out.println(Thread.currentThread());
            TimeUnit.SECONDS.sleep(15);
            return 1;
        };


        Callable<Integer> callable3 = () -> {
            System.out.println(Thread.currentThread());
            TimeUnit.SECONDS.sleep(20);
            return 1;
        };

        long start = System.currentTimeMillis();

        try {
            List<Future<Integer>> invoked = poolExecutor.invokeAll(Arrays.asList(callable1, callable2, callable3),
                    15L, TimeUnit.SECONDS);
            for (Future<Integer> future : invoked) {
                try {
                    Integer a = future.get();
                } catch (Throwable e) {
                    e.printStackTrace();
                }

            }
        } catch (Throwable e) {
            System.out.println("12");
            e.printStackTrace();
        }

        long d = System.currentTimeMillis() - start;
        System.out.println(d / 1000);

    }

}

运行结果:总超时时间为预设值的15s。

线程池invokeAll的原理其实是动态改动了java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit)设置的超时时间,每次都会设置为:任务截止时间减去当前时间,如下图源码所示:

正确做法:使用CompletableFuture

使用CompletableFuture.allOf(......).get(15L, TimeUnit.SECONDS),也能设置总任务超时时间。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.renzhikeji.demo;

import java.util.concurrent.*;
import java.util.function.Supplier;

/**
 * @author 认知科技技术团队
 * 微信公众号:认知科技技术团队
 */
public class JdkDemo {
    private static final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), Thread::new, new ThreadPoolExecutor.AbortPolicy() {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            System.out.println("rejectedExecution");
            super.rejectedExecution(r, e);
        }
    });

    public static void main(String[] args) {


        Supplier<Integer> callable1 = () -> {
            System.out.println(Thread.currentThread());
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 1;
        };
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(callable1, poolExecutor);

        Supplier<Integer> callable2 = () -> {
            System.out.println(Thread.currentThread());
            try {
                TimeUnit.SECONDS.sleep(15);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 1;
        };
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(callable2, poolExecutor);


        Supplier<Integer> callable3 = () -> {
            System.out.println(Thread.currentThread());
            try {
                TimeUnit.SECONDS.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 1;
        };

        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(callable3, poolExecutor);

        long start = System.currentTimeMillis();


        try {
            CompletableFuture.allOf(future2, future2, future3).get(15L, TimeUnit.SECONDS);
        } catch (Throwable e) {
            e.printStackTrace();
        }


        long d = System.currentTimeMillis() - start;
        System.out.println(d / 1000);

        try {
            Integer integer = future1.get();
            System.out.println("future1");
        } catch (Throwable e) {
            e.printStackTrace();

        }
        try {
            Integer integer = future2.get();
            System.out.println("future2");
        } catch (Throwable e) {
            e.printStackTrace();

        }
        try {
            Integer integer = future3.get();
            System.out.println("future3");
        } catch (Throwable e) {
            e.printStackTrace();

        }

    }

}

执行结果:任务1、任务2执行完了,任务3超时了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 认知科技技术团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java并发编程(08):Executor线程池框架
Executor系统中,将线程任务提交和任务执行进行了解耦的设计,Executor有各种功能强大的实现类,提供便捷方式来提交任务并且获取任务执行结果,封装了任务执行的过程,不再需要Thread().start()方式,显式创建线程并关联执行任务。
知了一笑
2020/09/01
3560
Java并发编程(08):Executor线程池框架
笑了,面试官问我知不知道异步编程的Future。
文本主要分享这个 future 的调用方式,不讲 Dubbo 框架,这里只是一个引子。
why技术
2020/08/13
4650
笑了,面试官问我知不知道异步编程的Future。
CompletableFuture 异步多线程,那叫一个优雅
虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,我们必须使用Future.get()的方式阻塞调用线程,或者使用轮询方式判断 Future.isDone 任务是否结束,再获取结果。
程序员大彬
2023/03/01
1.7K0
CompletableFuture 异步多线程,那叫一个优雅
java---多线程[重点(下)]
第三种创建线程的方式,使用Callable创建的线程可以有一个返回值。Interface Callable<V>它是一个接口和Runnable一样。void run() 这方法是一个无返回值,无参数的方法。Callable 提供了一个call()返回可以返回一个值,这个值的类型由Callable<V>泛型决定。
用户10787181
2023/10/17
3400
java---多线程[重点(下)]
【并发编程】异步编程CompletableFuture实战
在JDK8之前,我们使用的Java多线程变成,主要是 Thread+Runnable 来完成,但是这种方式有个弊端就是没有返回值。如果想要返回值怎么办呢,大多数人就会想到 Callable + Thread 的方式来获取到返回值。
互联网小阿祥
2023/05/28
1.2K0
【并发编程】异步编程CompletableFuture实战
多线程的几种创建方式
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。 Future提供了三种功能:   1)判断任务是否完成;   2)能够中断任务;   3)能够获取任务执行结果。 FutureTask是Future和Runable的实现
周杰伦本人
2022/10/25
3660
多线程的几种创建方式
论异步编程的正确姿势:十个接口的活现在只需要一个接口就能搞定!
来源:https://juejin.cn/post/6844903901758504973
猿天地
2021/11/08
3010
JUC并发编程
业务:普通的线程代码 Thread Runnable: 没有返回值、效率比Callable 相对较低!
JokerDJ
2023/11/27
3140
JUC并发编程
聊聊Java中CompletableFuture的使用
CompletableFuture是java8引入的一个异步类,它最大的优势是可以在创建的对象中传入一个回调对象,在任务结束后(done或throw exception),自动调用回调对象的回调方法,而不用让主线程阻塞。
jinjunzhu
2020/08/20
9200
一次性解决老大难问题:线程治理 Futrue、Callable接口、CompletableFuture
线程治理最重要的是线程池了,之前我讲过,但是,还有两大法宝就是future 和 callable
Joseph_青椒
2023/08/26
1.1K0
一次性解决老大难问题:线程治理 Futrue、Callable接口、CompletableFuture
了解多线程以及如何进行并发编程?
硬件层面软件层面并发和并行JAVA中的线程线程的基础源码分析线程的启动线程的中断异步并发异步 Future异步Callback异步编排 CompletableFuture小结更多
架构探险之道
2020/07/07
8190
了解多线程以及如何进行并发编程?
死磕Juc(一)之CompletableFuture
Future接口定义了操作异步任务执行一些方法,如获取异步任务的执行结果、取消任务的执行、判断任
yuanshuai
2022/08/17
5740
死磕Juc(一)之CompletableFuture
多线程进阶--JUC并发编程
https://blog.csdn.net/weixin_44502509/article/details/106872957
hhss
2021/02/12
6580
多线程进阶--JUC并发编程
java基础第十六篇之多线程
1:线程的概念 进程(任务):一个正在运行的程序 进程的调度:CPU来决定什么时候该运行哪个进程 (时间片轮流法) 线程在一个应用程序中,同时,有多个不同的执行路径,是进程中的实际运作单位。 好处是提高程序效率。
海仔
2019/08/05
3060
CompletableFuture探秘:解锁Java并发编程的新境界
在Java的世界中,异步编程是应对高并发的利器,而CompletableFuture则是这个工具箱中的瑰宝。就像是一把打开未来之门的钥匙,CompletableFuture为我们提供了更高效、更灵活的并发编程方式。让我们一同踏上这段深入CompletableFuture的实战之旅,发现异步编程的魔力。
一只牛博
2025/05/31
1050
有了Future为什么还要CompletableFuture?
cheese
2024/02/06
2720
有了Future为什么还要CompletableFuture?
线程池-异步任务(I)
声明一个线程池,这个没问题,前面说过,用static也行,用spring来托管也可以
顾老师的猫
2024/10/29
1360
细数线程池的10个坑
日常开发中,为了更好管理线程资源,减少创建线程和销毁线程的资源损耗,我们会使用线程池来执行一些异步任务。但是线程池使用不当,就可能会引发生产事故。今天田螺哥跟大家聊聊线程池的10个坑。大家看完肯定会有帮助的~
捡田螺的小男孩
2023/02/24
1.1K0
细数线程池的10个坑
Juc并发编程12——2万字深入源码:线程池这篇真的讲解的透透的了
本文将介绍常见的线程池的使用方法,介绍线程池的参数、拒绝策略、返回参数获取以及定时调度。
半旧518
2022/10/26
1820
Juc并发编程12——2万字深入源码:线程池这篇真的讲解的透透的了
【第十四篇】商城系统-异步处理利器-CompletableFuture
  上面的三种获取线程的方法是直接获取,没有对线程做相关的管理,这时可以通过线程池来更加高效的管理线程对象。
用户4919348
2022/10/04
3930
【第十四篇】商城系统-异步处理利器-CompletableFuture
相关推荐
Java并发编程(08):Executor线程池框架
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档