线程池

频繁的创建、销毁线程和线程池,会给系统带来额外的开销。未经池化及统一管理的线程,则会导致系统内线程数上限不可控。这种情况下,随着访问数增加,系统内线程数持续增长,CPU负载逐步提高。极端情况下,甚至可能会导致CPU资源被吃满,整个服务不可用。

为了解决上述问题,可增加统一线程池配置,替换掉自建线程和线程池。下面介绍几种创建线程池的方式。

Executors工具类

java 复制代码
// 创建一个固定数量线程的线程池 还有其他线程池,小伙伴们可以自己点进源码看看哦
ExecutorService executorService = Executors.newFixedThreadPool(1);

优点:简单,方便

缺点 :Excutors创建的线程池用的无界队列,有oom的风险

自定义线程池

ExecutorService 属于并发包concurrent下

java 复制代码
// 小伙伴们要记住这7个参数具体的含义哦
ExecutorService executor = new ThreadPoolExecutor(
        3, 5, 200,
        TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(5), // 阻塞队列
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());

7大核心参数,简单理解

corePoolSize: 核心线程数量(常驻),永不淘汰

maximumPoolSize: 最大线程池数量,>= corePoolSize数量

keepAliveTime: 当maximumPoolSize > corePoolSize时,线程空闲时的存活时间

TimeUnit unit: keepAliveTime存活时间的单位

workQueue: 阻塞队列的类型

threadFactory: 线程池工厂

handler: 拒绝策略

如何优雅停机

java 复制代码
// 创建一个固定大小的线程池,核心线程数=最大线程数=2
ThreadPoolExecutor executorService = new ThreadPoolExecutor(
        2, 2, 0L, TimeUnit.MILLISECONDS,  
        new LinkedBlockingQueue<>(2), // 队列大小为2  
        new ThreadPoolExecutor.CallerRunsPolicy() // 设置拒绝策略为CallerRunsPolicy  
);
// 提交5个任务到线程池  
for (int i = 0; i < 5; i++) {  
    final int taskId = i;
    executorService.execute(() -> {  
        System.out.println("Executing task " + taskId + " by " + Thread.currentThread().getName());  
        try {  
            // 模拟耗时任务  
            Thread.sleep(1000);  
        } catch (InterruptedException e) {  
            Thread.currentThread().interrupt();  
        }  
    });  
}  

// 等待所有任务完成 优雅停机 
executorService.shutdown(); // 不接受新任务,但会等待已提交任务完成  
try {  
    if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {  // 等待任务的完成,如果超出时间还未完成,则会返回false,如果提前完成返回true
        executorService.shutdownNow(); // 取消所有任务  
    }  
} catch (InterruptedException e) {  
    executorService.shutdownNow(); // 在中断的情况下也取消所有任务  
}  

由于本次使用了CallerRunsPolicy拒绝策略,所以第五个任务执行是由main线程来执行的。它的行为是:如果线程池和队列都满了,那么就直接在提交任务的线程中运行这个任务

spring中线程池

ThreadPoolTaskExecutor所属spring管理

java 复制代码
@Configuration
@EnableAsync
public class ThreadPoolConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        return myExecutor();
    }

    @Bean("my-myExecutor")
    @Primary
    public ThreadPoolTaskExecutor myExecutor() {
        // ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,2,4L,);
        // spring所管理的,赋予了优雅停机 ExecutorConfigurationSupport -> DisposableBean
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 优雅停机 当spring关闭时,调用destroy时会调用shutdown方法
        executor.setWaitForTasksToCompleteOnShutdown(true); 
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(200);
        executor.setThreadNamePrefix("myproject-executor-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//满了调用线程执行,认为重要任务
        // 添加异常处理,因为如果添加,出现异常时会被吃掉,不打印
        executor.setThreadFactory(new MyThreadFactory(executor));
        executor.initialize();
        return executor;
    }
    
    // 当想要使用时:
    @Autowired
    @Qualifier("my-myExecutor")  // 指定bean的名称
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
}
相关推荐
盛派网络小助手1 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
∝请叫*我简单先生1 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
zquwei2 小时前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring
dessler3 小时前
Docker-run命令详细讲解
linux·运维·后端·docker
Q_19284999063 小时前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
ZSYP-S4 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
Yuan_o_5 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
程序员一诺5 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
DT辰白6 小时前
如何解决基于 Redis 的网关鉴权导致的 RESTful API 拦截问题?
后端·微服务·架构