1.1 线程池概念
在处理大量并发任务的时候,如果按照传统的方式,来一个任务请求,创建一个线程来进行任务的处理,大量线程的创建和销毁,将消耗过多的系统资源,还增加了线程上下文(运行环境)切换的开销,而通过线程池技术就可以很好地解决这些问题。
线程池技术通过在系统中预先创建一定数量的线程,当任务请求到来时从线程池中分配一个预先创建的线程去处理任务,线程在处理完任务之后并不会销毁,而是把线程归还到线程池中,继续为后续的任务提供服务。
1.2 线程池的特点
1、线程复用:线程池会在内部维护一定数量的线程,并在需要时重复使用这些线程来执行任务,避免频繁地创建和销毁线程,从而提高性能和效率。
2、控制并发性:对于多核处理器,由于多线程被分配到多个处理器中,提高并行处理的效率。线程池可以控制并发执行的线程数量,通过设定线程池的大小来限制系统中同时执行的线程数量,避免资源的过度占用和线程竞争导致的性能下降。
3、任务排队:当线程池中的线程已经全部被占用时,新提交的任务会被放入一个任务队列中进行排队等待执行。排队机制可以根据具体线程池的实现,选择不同的队列类型,如有界队列或无界队列。
4、提高任务的响应速度,当任务到达时,不需要等待线程创建就能立即执行任务。
5、提供线程管理和监控:线程池提供了易于管理和监控线程的方式,可以设置线程的属性、监控线程的状态、统计任务执行情况等。
6、避免资源的过度消耗:线程池可以通过限制线程数量和排队机制来避免过度消耗资源。当系统负载过高时,线程池可以根据配置策略进行线程数量的自动调整,以保持系统的稳定性和性能。
7、提高任务执行的灵活性:线程池可以接受不同类型的任务,并对这些任务进行执行调度。它提供了一系列提交任务的方法,如execute()、submit(),同时还支持定时执行和周期性执行任务的能力。
1.3 按应用场景分类
1、FixedThreadPool:
固定线程池:线程池中的线程数量固定,这些线程会一直存在,不会随任务的增加或减少而动态调整,超出的任务会在队列中等待。
使用场景:任务量比较固定但耗时较长的任务。
2、CachedThreadPool:
缓存线程池:可根据需要创建新线程的线程池,如果新任务到达,但线程池中没有可用的线程,则创建一个新线程并添加到池中,如果有被使用完但是还没销毁的线程,就复用该线程。线程池中超过60s未使用的线程,将会被移除和销毁。
使用场景:任务量大但耗时少的任务。
3、WorkStealingPool:
工作窃取线程池:创建一个拥有多个任务队列(以便减少连接数)的线程池;
使用场景:会创建一个含有足够多线程的线程池,来维持相应的并行级别,它会通过工作窃取的方式,使得多核的CPU不会闲置,总会有活着的线程让CPU去运行。如果当前工作线程处理完自己本地任务队列中的任务时,就会去全局队列或其他工作线程的队列里面查找工作任务,帮助它们完成。利用Work Stealing,可以更好实现负载均衡。
4、ScheduledThreadPool:
计划线程池(定时线程池、调度线程池)
使用场景:定时以及周期性执行任务。
1.4 线程池模式
半同步/半异步模式又称为生产者消费者模式,是比较常见的实现方式,比较简单。分为同步层、队列层、异步层三层。同步层的主线程处理工作任务并存入工作队列,工作线程从同步队列取出任务进行处理,如果工作队列为空,则取不到任务的工作线程进入挂起状态。由于线程间有数据通信,因此不适用于大数据量交换的场合。
1.5 线程池实现的关键技术分析
线程池由三层组成:同步服务层、排队层和异步服务层,其中排队层居于核心地位,因为上层会将任务添加到排队层中,异步服务层同时也会取出任务,这里有一个同步的过程。在实现时,排队层就是一个同步队列,允许多个线程同时去添加或取出任务,并且要保证操作过程是安全的。
线程池有两个活动过程,一个是往同步队列中添加任务的过程,另一个是从同步队列中取出任务的过程。
从活动图中可以看到线程池的活动过程,一开始线程池会启动一定数量的线程,这些线程属于异步层,主要用来并行处理排队层中的任务,如果排队层中的任务数为空,则这些线程等待任务的到来,如果发现排队层中有任务了,线程池则会从等待的这些线程中唤醒一个来处理新任务。
同步服务层则会不断地将新的任务添加到同步排队中,有可能上层的任务非常多,而任务又是非常耗时的,这时,异步层中的线程处理不过来,则同步排队层中的任务会不断增加,如果同步排队层不加上限控制,则可能会导致排队层中的任务过多,内存暴涨的问题。因此,排队层需要加上限的控制,当排队层中的任务数达到上限时,就不让上层的任务添加进来,起到限制和保护的作用。