hello,伙伴们,我们继续java多线程实现方式的学习,本节中我们通过线程池来实现多线程。那为什么使用线程池呢?使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者 “过度切换”的问题。在阿里开发手册中明确线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让开发的同学更加明确线程池的运行规则,规避资源耗尽的风险。那么我们接下来创建一个线程池实际用一下:
@Configuration @EnableAsync public class ThreadPoolTaskConfig { /** * 默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务, * 当线程池中线程数未达到corePoolSize时,每来一个任务就会创建一个线程来执行 即使线程池中有空闲的线程, * 当线程池中的线程数目达到corePoolSize并且缓存队列未满,就会把到达的任务放到缓存队列当中; * 当缓存队列满了,就继续创建线程来执行新来的任务,当线程数量大于等于maxPoolSize后,开始使用拒绝策略拒绝 */ /** 核心线程数 */ private static final int corePoolSize = 6; /** 最大线程数 */ private static final int maxPoolSize = 12; /** 允许线程空闲时间(单位:默认为秒) */ private static final int keepAliveTime = 10; /** 缓冲队列大小 */ private static final int queueCapacity = 10; @Bean("taskExecutor") public ThreadPoolTaskExecutor taskExecutor(){ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setQueueCapacity(queueCapacity); executor.setKeepAliveSeconds(keepAliveTime); //线程池对拒绝任务的处理策略, CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 初始化 executor.initialize(); return executor; } }
接下来我们在需要执行多线程的地方使用该线程池:
/** * 异步添加操作日志记录 */ @Async("taskExecutor")(开启异步执行 并通过“taskExecutor”来 指定使用我们创建的线程池中线程异步执行) public void addLogRecord(){ LogRecord logRecord = new LogRecord(); // 省略 日志记录业务逻辑 logRecordRepository.save(logRecord); }
小总结:当我们通过定义线程池来实现多线程时,每当有任务提交过来判断逻辑,线程池中线程数和 corePoolSize大小关系–> workQueue是否已满 –>线程池中线程数和 maximumPoolSize大小关系。当线程池中线程数>=maximumPoolSize时,接下来要执行用户自定义的任务拒绝策略,任务拒绝策略一共有下面四种:
AbortPolicy:直接抛出异常,这是默认策略;
CallerRunsPolicy:用调用者所在的线程来执行任务;
DiscardOldestPolicy:丢弃阻塞队列中最靠前的任务,并执行当前任务;
DiscardPolicy:直接丢弃任务;以上就是通过线程池实现多线程的使用。
伙伴们,我们下期见!