线上问题方案:Future自定义异步超时处理

1 场景复现

后台服务没有在预期时间内返回结果,并出现持续等待、资源一直占用等异常状况。
如下载Excel时查询的数据量多并且关联的表较多,此时查询耗时高且下载的Excle文件占用内存较大,
后台服务器尚未处理完成当前请求,前端又重新发送请求,恶性循环,最终导致IO异常。

2 处理方案

对耗费资源较多的请求配置超时处理,即设定超时时间,在指定的时间内程序没有完成请求任务,抛出异常,并捕获异常,返回给调用方提示信息:处理超时,并停止当前的线程任务,释放资源,等待处理下一次请求。

3 超时处理样例

3.1 线程池

package com.monkey.java_study.common.config;

import com.monkey.java_study.common.constant.ThreadPoolConstant;

import java.util.concurrent.*;

/**
 * 线程池配置.
 *
 * @author xindaqi
 * @date 2021-11-24 15:31
 */
public class ThreadPoolConfig {

    /**
     * 线程池
     */
    public static ExecutorService threadPoolExecutorGenerate = new ThreadPoolExecutor(
            ThreadPoolConstant.CORE_THREAD_NUM,
            ThreadPoolConstant.MAX_THREAD_NUM,
            ThreadPoolConstant.KEEP_ALIVE_TIME_SECONDS,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(ThreadPoolConstant.QUEUE_LENGTH),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());
}

3.2 方案

使用Future接口自定义超时处理。测试方案如下:

  • 使用线程异步执行特定的方法;
  • Future作为异步执行方法的返回值;
  • 通过get方法设定超时时间,当方法执行时间超过设定值后,抛出异常,手动捕获该异常,自定义需要返回的信息。

3.3 测试样例

package com.monkey.java_study.thread;

import com.monkey.java_study.common.config.ThreadPoolConfig;
import com.monkey.java_study.common.constant.BooleanConstant;
import com.monkey.java_study.common.constant.PeriodConstant;
import com.monkey.java_study.common.entity.UserEntity;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * Future测试.
 *
 * @author xindaqi
 * @date 2021-11-24 15:30
 */
public class FutureTest {

    private static final Logger logger = LogManager.getLogger(FutureTest.class);

    /**
     * 测试方法.
     *
     * @param userEntityList 用户实体信息列表
     * @throws Exception
     */
    static void test(List<UserEntity> userEntityList) throws Exception {
        userEntityList.forEach(s -> s.setSex("male"));
        // 延迟2秒
        Thread.sleep(2 * PeriodConstant.MILLISECOND_1000);
    }

    public static void main(String[] args) {

        // 用户实体列表
        List<UserEntity> userEntityList = new ArrayList<>();
        userEntityList.add(new UserEntity("1", "111", "male"));
        userEntityList.add(new UserEntity("2", "222", "female"));
        userEntityList.add(new UserEntity("3", "333", "male"));
        userEntityList.add(new UserEntity("4", "444", "female"));

        // 线程池:执行测试方法
        Future<?> testFuture = ThreadPoolConfig.threadPoolExecutorGenerate.submit(() -> {
            try {
                FutureTest.test(userEntityList);
            } catch (Exception ex) {
                logger.info("><><><><><><><><><><线程池异常:", ex);
                throw new RuntimeException(ex);
            }
        });

        try {
            /**
             * 设置超时时间:1秒,
             * 当方法执行时间超过1秒时,抛出异常
             * 测试方法:手动延迟2秒钟,测试抛出异常
             */
            testFuture.get(1, TimeUnit.SECONDS);
            logger.info(">>>>>>>>>正常执行, userEntity:{}", userEntityList);
        } catch(Exception ex) {
            testFuture.cancel(BooleanConstant.TRUE);
            logger.info("><><><><><><><><><><超时");
            throw new RuntimeException(ex);
        } finally {
            // 关闭线程池,测试样例,程序最后关闭线程池,后台服务中,一般不手动关闭线程池
            ThreadPoolConfig.threadPoolExecutorGenerate.shutdown();
        }
    }
}

测试结果如图3.1所示。由图3.1可知,通过Future.get()方法配置线程执行时间,超过设定时间后,程序主动抛出异常,此时,开发者手动捕获该异常,自定义异常处理逻辑。
在这里插入图片描述

图3.1 超时处理结果
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天然玩家

坚持才能做到极致

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值