【073期】Spring Boot 项目 @Async 默认线程池导致 OOM 问题如何解决?

最后

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

boolean debug = logger.isDebugEnabled();

synchronized (this.monitor) {

boolean interrupted = false;

while (this.concurrencyCount >= this.concurrencyLimit) {

if (interrupted) {

throw new IllegalStateException("Thread was interrupted while waiting for invocation access, " +

“but concurrency limit still does not allow for entering”);

}

if (debug) {

logger.debug("Concurrency count " + this.concurrencyCount +

" has reached limit " + this.concurrencyLimit + " - blocking");

}

try {

this.monitor.wait();

}

catch (InterruptedException ex) {

// Re-interrupt current thread, to allow other threads to react.

Thread.currentThread().interrupt();

interrupted = true;

}

}

if (debug) {

logger.debug("Entering throttle at concurrency count " + this.concurrencyCount);

}

this.concurrencyCount++;

}

}

}

线程任务执行完毕后,当前执行线程数会减一,会调用monitor对象的notify方法,唤醒等待状态下的线程,等待状态下的线程会竞争monitor锁,竞争到,会继续执行线程任务。

protected void afterAccess() {

if (this.concurrencyLimit >= 0) {

synchronized (this.monitor) {

this.concurrencyCount–;

if (logger.isDebugEnabled()) {

logger.debug("Returning from throttle at concurrency count " + this.concurrencyCount);

}

this.monitor.notify();

}

}

}

虽然看了源码了解了SimpleAsyncTaskExecutor有限流机制,实践出真知,我们还是测试下:

一、测试未开启限流机制下,我们启动20个线程去调用异步方法,查看Java VisualVM工具如下:

二、测试开启限流机制,开启限流机制的代码如下:

@Configuration

@EnableAsync

public class AsyncCommonConfig extends AsyncConfigurerSupport {

@Override

public Executor getAsyncExecutor() {

SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();

//设置允许同时执行的线程数为10

executor.setConcurrencyLimit(10);

return executor;

}

}

同样,我们启动20个线程去调用异步方法,查看Java VisualVM工具如下:

通过上面验证可知:

1.开启限流情况下,能有效控制应用线程数

2.虽然可以有效控制线程数,但执行效率会降低,会出现主线程等待,线程竞争的情况。

3.限流机制适用于任务处理比较快的场景,对于应用处理时间比较慢的场景并不适用。==

最终解决办法:

1.自定义线程池,使用LinkedBlockingQueue阻塞队列来限定线程池的上限

2.定义拒绝策略,如果队列满了,则拒绝处理该任务,打印日志,代码如下:

public class AsyncConfig implements AsyncConfigurer{

private Logger logger = LogManager.getLogger();

@Value(“${thread.pool.corePoolSize:10}”)

private int corePoolSize;

@Value(“${thread.pool.maxPoolSize:20}”)

private int maxPoolSize;

@Value(“${thread.pool.keepAliveSeconds:4}”)

private int keepAliveSeconds;

@Value(“${thread.pool.queueCapacity:512}”)

private int queueCapacity;

@Override

public Executor getAsyncExecutor() {

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

executor.setCorePoolSize(corePoolSize);

executor.setMaxPoolSize(maxPoolSize);

executor.setKeepAliveSeconds(keepAliveSeconds);

executor.setQueueCapacity(queueCapacity);

executor.setRejectedExecutionHandler((Runnable r, ThreadPoolExecutor exe) -> {

logger.warn(“当前任务线程池队列已满.”);

});

executor.initialize();

return executor;

}

@Override

public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {

return new AsyncUncaughtExceptionHandler() {

@Override

public void handleUncaughtException(Throwable ex , Method method , Object… params) {

logger.error(“线程池执行任务发生未知异常.”, ex);

}

};

}

}

最后

这份《“java高分面试指南”-25分类227页1000+题50w+字解析》同样可分享给有需要的朋友,感兴趣的伙伴们可挑战一下自我,在不看答案解析的情况,测试测试自己的解题水平,这样也能达到事半功倍的效果!(好东西要大家一起看才香)

image

image

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

[外链图片转存中…(img-IJKgFik5-1715475812702)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 26
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值