线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价
业务场景:打开微信要查询、转账、发红包等,后台要处理相关业务时,就需要前台发送数据
- 当前服务端要处理很多业务,先通过网络把客户端 的(请求类型和请求数据) 请求发送给服务端,
- 客户端在发送时,明确该请求是做什么业务和提交的数据
- 服务端拿到请求后开始处理
内部具体实现
同一时刻,多个客户端访问服务端
- 方法1.服务端对于每个客户的请求,挨个创建线程处理,但是在一个程序中不断创建线程并不现实,
一个程序占用的内存越来越多,有可能会被操作系统强杀掉;- 方法2.服务端有一个接收线程(只接收不处理)和一个发送线程,
. 接收线程接收到数据,数据放入队列1(有两个线程安全队列)
. 业务线程拿到队列1中数据并处理,完成后将数据放入队列2
. 发送线程从队列2中拿走数据
- 因为客户端的请求数量和时间是不固定的,所以为了节约资源需要判断初始化业务线程的数量
线程池就是一堆业务线程
线程池:
- 一开始,线程池里就有很多线程(业务线程),
- 线程池中有一个线程安全队列+一堆线程(执行的是同一个入口函数,线程代码是一样的)
- 但是每个队列中存放的请求是不同的,业务线程如何区分这些处理请求?
使队列中每个元素定义的类型是:待处理的数据+如何处理的方法(处理函数)
线程池要想处理广泛的业务,队列中的类型就是通用的,
线程池的实现
线程池由一个线程池队列和业务线程组成
- 由操作员在main函数中启动Start函数创建业务线程;
- main函数再调用Push函数将请求放入线程池队列中;
- 业务线程调用pop函数从队列中拿到数据,并使用Deal函数对每一个请求进行处理;
线程池成员变量和成员函数
Start启动函数
业务线程
还可以添加一个管理者线程,周期性的对队列中任务的个数和处于工作的业务线程个数进行检测,当任务减少时,将空闲的阻塞线程通过管理者线程进行销毁;
Push和Pop函数
- Push函数相当于一个生产者,在对队列访问时,需要进行加锁操作,当队列满了,就阻塞
- 任务队列为空,使用条件变量阻塞消费者线程