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()方法配置线程执行时间,超过设定时间后,程序主动抛出异常,此时,开发者手动捕获该异常,自定义异常处理逻辑。