使用线程池之后,不需要频繁的去创建和销毁线程(比如项目中手动创建线程,new Thread 类,我们可以把创建和销毁的线程的过程去掉),从而让线程得到重复的使用。并且可以对线程进行统一的管理。
在一个网页或应用程序中,每次请求都需要创建新的线程去处理,所以频繁的创建处理这些请求的线程非常消耗资源,为每个请求创建新线程将花费更多的时间,在创建和销毁线程时花费更多的系统资源。因此同时创建太多线程的JVM可能会导致系统内存不足,这就需要限制要创建的线程数,也就是需要使用到线程池。
springboot框架提供了@Async注解,帮助我们更方便的将业务逻辑提交到线程池中异步执行。

【图片转自网络】
创建线程池时,需要为他指定一个核心线程数,以及最大线程数,然后还需要给他配置一个任务队列。
当我们把任务添加到线程池时,首先线程池会去判断是否有剩余的核心线程,如果有,他就会调用核心线程去执行本次任务。如果没有,他会去判断任务队列是否已满,如果没满,那他就会把本次任务添加到任务队列,否则他会去判断线程池中的最大线程数是否已满,如果已满,那么他会去执行他的一个拒绝策略,否则他会去调用非核心线程去执行本次任务。
交由主线程执行。如果执行程序已关闭,则会丢弃该任务)抛弃当前任务;会导致被抛弃的任务无法再次被执行)抛弃工作队列中旧的任务,将新任务添加进队列;会导致被丢弃的任务无法再次被执行)在springboot中为我们提供了线程池类,叫ThreadPoolTaskExecutor。
线程依赖于cpu,所以核心线程数量一般设置成cpu个数的两倍,最大线程数一般设置为cpu个数的四倍。
/config/ThreadPoolConfig.java
@Configuration
public class ThreadPoolConfig {// 获取服务器的cpu个数private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();// 获取cpu个数private static final int COUR_SIZE = CPU_COUNT * 2;private static final int MAX_COUR_SIZE = CPU_COUNT * 4;// 接下来配置一个bean,配置线程池。@Beanpublic ThreadPoolTaskExecutor threadPoolTaskExecutor() {ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();threadPoolTaskExecutor.setCorePoolSize(COUR_SIZE);// 设置核心线程数threadPoolTaskExecutor.setMaxPoolSize(MAX_COUR_SIZE);// 配置最大线程数threadPoolTaskExecutor.setQueueCapacity(MAX_COUR_SIZE * 4);// 配置队列容量(这里设置成最大线程数的四倍)threadPoolTaskExecutor.setThreadNamePrefix("test-thread");// 给线程池设置名称threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 设置任务的拒绝策略return threadPoolTaskExecutor;}
}
/controller/ThreadPoolController.java
@RestController
public class ThreadPoolController {private final Logger logger = LoggerFactory.getLogger(ThreadPoolController.class);@Resourceprivate ThreadPoolTaskExecutor threadPoolTaskExecutor;@GetMapping("/thread")public Result testThread() {threadPoolTaskExecutor.execute(() -> {Thread.sleep(10000);// 为了演示方便,让变成休眠10秒logger.info("执行线程池任务");logger.info(Thread.currentThread().getName());//打印线程名称});// 需要传递Runnable对象logger.info("主线程名称:{}", Thread.currentThread().getName());//再打印主线程名称return Result.success("success");}
}
启动服务后,访问localhost:8080/thread。然后看控制台,能看出先打印了主线程名称,然后过了10秒,打印了"执行线程池任务"和子线程名称。
主线程名称:http-nio-8002-exec-1
执行线程池任务 // 10秒后...
test-thread1 // 10秒后...
/config/ThreadPoolConfig.java
@Configuration
@EnableAsync//开启异步调用
public class ThreadPoolConfig {...
}
创建controller,开发一个http服务接口,里面会调用service层的服务。将Service层的服务异步化,这样每次调用都会都被提交到线程池异步执行。
/service/TestService.java
public interface TestService {void test();
}
/service/TestServiceImpl.java
@Service
public class TestServiceImpl implements TestService {private static final Logger logger = LoggerFactory.getLogger(TestServiceImpl.class);@Override@Async("threadPoolTaskExecutor")// 提交到线程池中去处理public void test() {logger.info("start service");try{Thread.sleep(1000);}catch(Exception e){e.printStackTrace();}logger.info("end service");}
}
/controller/ThreadPoolController.java
@RestController
public class ThreadPoolController {private static final Logger logger = LoggerFactory.getLogger(ThreadPoolController.class);@Autowiredprivate TestService testService;@GetMapping("/thread")public String testThread() {logger.info("start controller");testService.test();logger.info("end controller");}
}