springboot 线程池

为什么要使用线程池

使用线程池之后,不需要频繁的去创建和销毁线程(比如项目中手动创建线程,new Thread 类,我们可以把创建和销毁的线程的过程去掉),从而让线程得到重复的使用。并且可以对线程进行统一的管理。

在一个网页或应用程序中,每次请求都需要创建新的线程去处理,所以频繁的创建处理这些请求的线程非常消耗资源,为每个请求创建新线程将花费更多的时间,在创建和销毁线程时花费更多的系统资源。因此同时创建太多线程的JVM可能会导致系统内存不足,这就需要限制要创建的线程数,也就是需要使用到线程池。

springboot框架提供了@Async注解,帮助我们更方便的将业务逻辑提交到线程池中异步执行。

线程池的使用场景

  • 请求量大,任务执行时间短的业务。
  • 无论请求量大小,任务执行时间长的业务。

线程池工作原理

在这里插入图片描述
【图片转自网络】

创建线程池时,需要为他指定一个核心线程数,以及最大线程数,然后还需要给他配置一个任务队列

当我们把任务添加到线程池时,首先线程池会去判断是否有剩余的核心线程,如果有,他就会调用核心线程去执行本次任务。如果没有,他会去判断任务队列是否已满,如果没满,那他就会把本次任务添加到任务队列,否则他会去判断线程池中的最大线程数是否已满,如果已满,那么他会去执行他的一个拒绝策略,否则他会去调用非核心线程去执行本次任务

  • 主线程提交任务到线程池
  • 线程池判断当前线程池的线程数和核心线程数的大小,如果线程数小于核心线程数就新建线程处理任务;否则继续判断当前工作列队是否已满。
  • 如果当前工作队列未满,就将任务放到工作队列中,否则继续判断当前线程池的线程数和最大线程数的大小。
  • 如果当前线程池的线程数小于最大线程数,就新建线程处理请求,否则就执行拒绝策略。

线程池中的拒绝策略

  • AbortPolicy - 抛出RejectedExecutionException异常,终止任务。
  • CallerRunsPolicy - 使用调用线程执行任务。(交由主线程执行。如果执行程序已关闭,则会丢弃该任务
  • DiscardPolicy - 直接丢弃。(抛弃当前任务;会导致被抛弃的任务无法再次被执行
  • DiscardOldestPolicy - 丢弃队列最老任务,然后将本次任务添加到线程池。(抛弃工作队列中旧的任务,将新任务添加进队列;会导致被丢弃的任务无法再次被执行

简单使用

在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,配置线程池。
	@Bean
	public 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);

	@Resource
	private 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秒后...

将Service层的服务异步化

/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);

	@Autowired
    	private TestService testService;

	@GetMapping("/thread")
	public String testThread() {
		logger.info("start controller");
		testService.test();
		logger.info("end controller");
	}
}

  • 15
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Boot中的线程池主要是通过使用ThreadPoolTaskExecutor来实现的。ThreadPoolTaskExecutor是Spring基于java本身的线程池ThreadPoolExecutor做的二次封装,主要目的是为了在Spring框架中更方便地使用线程池。它是Spring中默认的线程池。 在Spring Boot中,我们可以使用@Async注解来开启异步任务,并通过@Async("pool")来指定线程池。如果没有指定线程池Spring将使用默认的线程池。另外,我们可以根据需求自定义线程池来处理任务。 配置Spring Boot中的线程池可以通过配置文件的形式实现。可以使用ThreadPoolTaskExecutor将线程池的bean注入到IOC容器中,并使用配置文件进行自动配置。在配置文件中,我们可以设置核心线程数、最大线程数、空闲线程存活时间、线程队列数量等参数。 总的来说,Spring Boot提供了方便且灵活的线程池配置方式,可以根据需求来选择默认的线程池或自定义线程池来处理异步任务。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Springboot自带线程池](https://blog.csdn.net/weixin_45874214/article/details/130446654)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值