springboot中使用线程池记录
仅供自己记录
JDK自带的线程池ThreadPoolExecutor;
Spring默认也是自带了一个线程池方便我们开发,它是ThreadPoolTaskExecutor;
Spring更加推荐我们开发者使用ThreadPoolTaskExecutor类来创建线程池,其本质是对java.util.concurrent.ThreadPoolExecutor的包装;
一般使用自己配置的线程池,配置方式如下
在application.properties中配置:
#业务使用pool配置
thread.pool.name=pool_query-
thread.pool.max.size=8
thread.pool.core.size=5
thread.pool.queue.size=10
thread.pool.alive.time=5
配置一个Config.java
import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.ThreadPoolExecutor; /** * @Description 工作线程池 */ @Configuration public class ThreadExecutorPool { @Value("${thread.pool.max.size}") private Integer maxSize; @Value("${thread.pool.core.size}") private Integer coreSize; @Value("${thread.pool.queue.size}") private Integer queueSize; @Value("${thread.pool.alive.time}") private Integer aliveTime; @Value("${thread.pool.name}") private String threadPoolName; @Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(coreSize);//线程池维护线程的最少数量 executor.setMaxPoolSize(maxSize);//线程池维护线程的最大数量 executor.setQueueCapacity(queueSize);//缓存队列 executor.setThreadNamePrefix(threadPoolName); executor.setKeepAliveSeconds(aliveTime);//允许的空闲时间 // 该方法用来设置线程池关闭的时候 等待 所有任务都完成后,再继续 销毁 其他的 Bean, executor.setWaitForTasksToCompleteOnShutdown(true); // 任务的等待时间 如果超过这个时间还没有销毁就 强制销毁,以确保应用最后能够被关闭,而不是阻塞住。 executor.setAwaitTerminationSeconds(300); //对拒绝task的处理策略 //rejection-policy:当pool已经达到max size的时候,如何处理新任务 //CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } }
常使用的两种方式
第一种:
在方法上添加@Async注解
stup1.在@SpringBootApplication启动类或者@Configuration注解类上 添加注解@EnableAsync启动多线程注解;
stup2.@Async这个方法开启异步多线程调用还需要将这个方法的类一定要交给Spring容器来管理(该方法写在另外一个类中,并且这个类使用到@Component注解)
关于注解失效需要注意以下几点
- 注解的方法必须是public方法
- 方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的,因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器。
- 异步方法使用注解@Async的返回值只能为void或者Future
第二种:
使用线程池 threadPool直接执行相关业务:
无返回值的任务使用public void execute(Runnable command) 方法提交;
有返回值的任务使用public <T> Future<T> submit(Callable) 方法提交。
import com.txj.query.utils.AsyncTest; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; /** * @description: */ @Slf4j @RestController @RequestMapping("/test") public class Test { @Autowired private ThreadPoolTaskExecutor threadPool; @Autowired private AsyncTest asyncTest; @PostMapping("test") public void test() { List<String> list = Arrays.asList(new String[] {"1", "2", "3"}); long start = System.currentTimeMillis(); List<Future<String>> futures = new ArrayList<>(); for (String sql : list) { System.out.println(sql+"开始执行:"+System.currentTimeMillis()); //第一种方式 Future<String> future1 = asyncTest.asyncTest("20220101", sql); //第二种方式 Future<String> future2 = (Future<String>) threadPool.submit(new Thread(() -> { asyncTest("20220101", sql); }));//submit有返回值 futures.add(future1); futures.add(future2); } List<String> response = new ArrayList<>(); for (Future future : futures) { String string = null; try { string = (String) future.get(); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (ExecutionException e) { throw new RuntimeException(e); } response.add(string); } System.out.println(response); System.out.println( String.format("任务执行成功,耗时{%s}毫秒", System.currentTimeMillis() - start)); }
public Future<String> asyncTest(String day, String sqls) { try { log.info(System.currentTimeMillis()+"开始执行,线程名称:{}", Thread.currentThread().getName()); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return new AsyncResult<>(String.format("这个是第{%s}个异步调用", sqls)); } }
import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Component; import java.util.concurrent.Future; /** * @description: */ @Slf4j @Component public class AsyncTest { @Async("myExecutor") public Future<String> asyncTest(String day, String sqls) { try { log.info(System.currentTimeMillis()+"开始执行,线程名称:{}", Thread.currentThread().getName()); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } return new AsyncResult<>(String.format("这个是第{%s}个异步调用", sqls)); } }