为什么要使用线程池
Posted 甜瓜瓜哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么要使用线程池相关的知识,希望对你有一定的参考价值。
Java线程的创建非常昂贵,需要JVM和OS(操作系统)配合完成大量的工作:
(1)必须为线程堆栈分配和初始化大量内存块,其中包含至少1MB的栈内存。
(2)需要进行系统调用,以便在OS(操作系统)中创建和注册本地线程。
由于创建线程和销毁线程的代价比较高,为了避免销毁和创建时带来的性能损耗,所以我们使用线程将其缓存起来。
Java高并发应用频繁创建和销毁线程的操作是非常低效的,而且是不被编程规范所允许的。
如何降低Java线程的创建成本?必须使用到线程池。线程池主要解决了以下两个问题:
(1)提升性能:线程池能独立负责线程的创建、维护和分配。在执行大量异步任务时,可以不需要自己创建线程,而是将任务交给线程池去调度。线程池能尽可能使用空闲的线程去执行异步任务,最大限度地对已经创建的线程进行复用,使得性能提升明显。
(2)线程管理:每个java线程池会保持一些基本的线程统计信息,例如完成的任务数量、空闲时间等,以便对线程进行有效管理,使得能对所接收到的异步任务进行高效调度。
参考资料:为什么要使用线程池?
线程池正确打开方式- 拌饭使用效果更佳
为什么要用线程池?什么是线程池?怎么使用?素质三连!!!
- 为什么要用线程池?
- 节约系统资源。避免反复创建销毁线程
- 提供异步操作。提高响应速度
- 方便管理线程
- 什么是线程池?
- 多线程使用的一种管理方式。池化技术能有效的利用和管理一些资源
- 怎么使用?
- 当然是配合springboot使用。皮一下??
- 整个过程梳理如下
- 让IOC容器加载和管理线程池 - 即配置完成之后,项目启动便加载配置
- 调用时使用注解 - 方便开发
线程池核心概念介绍
-
corePoolSize 正常持有的线程数。在使用线程时,如果池中的线程数小于该数,会创建新线程
-
maximumPoolSize 允许拥有的最大线程数。 池中核心线程core如果超出了,会放到阻塞队列中。队列如果也满了,会申请创建线程但数量不能超过该数值。如果超过了会执行拒绝策略
-
BlockingQueue<Runnable> workQueue 阻塞队列,用于存放等待资源的线程。
-
long keepAliveTime,TimeUnit unit 大于核心线程数的线程空闲之后的存活时间。活着浪费空气
实际配置如下
- 启动类上添加@EnableConfigurationProperties 注解。用于解析自定义配置属性。同时需要引入pom配置
-
@EnableConfigurationProperties public class MerchantApplication { public static void main(String[] args) { SpringApplication.run(MerchantApplication.class, args); } } <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
-
-
配置启动加载时需要的类。1.yml配置属性。2.配置类启动加载
-
// yml配置 task: pool: corePoolSize: 5 queueCapacity: 20 maxPoolSize: 10 keepAliveSeconds: 3 // 属性映射类 @Data @ConfigurationProperties(prefix = "task.pool") @Component public class TaskThreadPoolProperty { private int corePoolSize; private int maxPoolSize; private int keepAliveSeconds; private int queueCapacity; }
// 启动加载@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Resource
private TaskThreadPoolProperty config;
/**
* 默认使用 名为 taskExecutor 的线程池
*
* @return 初始化线程池
*/
@Bean("taskExecutor")
public Executor configTaskAsyncPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程池大小
executor.setCorePoolSize(config.getCorePoolSize());
//最大线程数
executor.setMaxPoolSize(config.getMaxPoolSize());
//队列容量
executor.setQueueCapacity(config.getQueueCapacity());
//活跃时间
executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
//线程名字前缀
executor.setThreadNamePrefix("Qc-");
// CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
-
- 调用 使用@Async注解调用
-
// 实现异步操作的类 @Component public class AsyncMessage { @Async public void sendMsgCL(String mobile, String content) { try { Thread.sleep(3000L); System.out.println("多线程异步执行" + " " + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } } // 测试异步的接口 @Resource private AsyncMessage message; @PostMapping("/data1") public void data() { message.sendMsgCL(null, null); System.out.println("haha"); }
-
- 注意点(如果不注意可能会让你多吃两碗)
- 配置yml自定义属性之后需要重启IDEA才能生效。
- 启动加载的bean命名为@Bean("taskExecutor") 。spring在调用线程池时默认的池子名称为taskExecutor
- 使用@Async注解可以指明使用的线程池
- 不能修饰静态static方法
- 异步类需要被IOC容器管理。即要使用注解类似@Component @Service 。。。等等
- 异步方法不能与被调用的异步方法在同一个类中
- @EnableAsync 记得加上。可以在启动类上也可以在配置类上。
以上是关于为什么要使用线程池的主要内容,如果未能解决你的问题,请参考以下文章