为什么需要线程池
当我们需要使用多个线程处理任务时,每个任务可以很快就执行完成,但是线程需要建立、销毁,非常消耗资源,也降低了效率,因此需要采用线程池,保留建立的线程、重复使用线程执行任务,会大大地提高效率。
JAVA SDK对线程的支持
从JAVA SDK 1.5版本开始,提供了Executor 接口来表示线程池,通过execute方法执行任务,另外 ExecutorService接口,实现了多线程池的管理方法,包括任务的提交、线程池关闭、是否关闭等方法。如下图:
详细说一下ExecutorService接口,它通过 submit方法提交一个任务执行,任务需要实现Runnable 接口,Runnable接口是一个包含run函数的接口。
通过shutdown方法可以等待线程池内任务执行后,关闭线程池。
通过shutdownNow方法不等待,直接关闭线程池。
如下代码所示:Runnable task = () -> {try {String threadName = Thread.currentThread().getName();TimeUnit.SECONDS.sleep(1);System.out.println(threadName);}catch(InterruptedException e){e.printStackTrace();}};ExecutorService service = Executors.newFixedThreadPool(5);try{service.submit(task);service.shutdown();service.awaitTermination(1,TimeUnit.SECONDS);}catch (Exception e){e.printStackTrace();}finally {if (!service.isTerminated()){System.err.println("Thread pool is not terminated");}service.shutdownNow();System.out.println("Thread poo is finish");}
Executors是在java.util.concurrent包中的创建线程池的工厂类,
提供了多种创建线程池对象的静态工厂方法。
如下图所示:
在Spring boot 中使用线程池
首先在application.yml配置文件中,增加线程池的配置信息,如下:app:pool:core-pool-size: 1max-pool-size: 2pool-queue-capacity: 500thread-name-prefix: opc-server-connection-thread
然后增加线程池配置类
注意在类定义前增加@EnableAsync 和@Component注解
在返回Executor的方法前面增加@Bean注解,并设置Bean的名称@ConfigurationProperties("app.pool")@EnableAsync@Component@Setter@Getterpublic class AppThreadPoolConfig {private Integer corePoolSize;private Integer maxPoolSize;private Integer poolQueueCapacity;private String threadNamePrefix;@Bean(name = "asyncServiceExecutor")public Executor asyncServiceExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//配置核心线程数executor.setCorePoolSize(corePoolSize);//配置最大线程数executor.setMaxPoolSize(maxPoolSize);//配置队列大小executor.setQueueCapacity(poolQueueCapacity);//配置线程池中的线程的名称前缀executor.setThreadNamePrefix(threadNamePrefix);// rejection-policy:当pool已经达到max size的时候,如何处理新任务// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//执行初始化executor.initialize();return executor;}}
最后,在需要使用线程池的地方获取线程池Bean,并提交任务。private Executor asyncServiceExecutor;asyncServiceExecutor = (Executor) ApplicationContextHelper.getBean("asyncServiceExecutor");asyncServiceExecutor.execute(()-> kafkaTemplate.send(kafkaTopicConfig.getTopicPrefix() + "_" + datasourceId, datasourceId.toString(), msg));
在上面的代码中,我们可以看到 使用 ThreadPoolTaskExecutor类进行线程池的建立,该类在org.springframework.scheduling.concurrent包中。
ThreadPoolTaskExecutor类实现了Executor 接口,如下图所示:
总结
本文探讨了为什么需要线程池、Java SDK对线程池的支持以及Spring Boot中如何使用线程池,有描述的不清楚的地方请留言互动交流。