Java高并发程序设计笔记(四)之线程池

时间:2022-02-28 05:41:52

一.线程池

多线程可以最大限度地发挥多核处理器的计算能力,提高生产系统的吞吐量和性能。但是若果不加控制和管理随意使用线程,对系统的性能反而会产生不利的影响。线程的创建和关闭也需要花费时间,如果为每一个小的任务都要创建一个线程,很有可能出现创建和销毁线程的时间远大于真实工作所消耗的时间,反而会得不偿失。

其次线程本身也要占用内存空间,大量的线程会抢占宝贵的内存资源,如果处理不当会导致内存溢出,大量的线程回收也会给GC带来压力。

在使用线程池后,创建线程变成了从线程池中获得空闲的线程,关闭线程变成了向池子归还线程。当需要使用线程时,可以从线程池中随便拿一个空闲线程,当完成工作时,并不着急关闭线程,而是将这个线程退回到线程池。

在java.util.concurrent包中,ThreadPoolExecutor表示一个线程池,Excutors类则扮演者线程池工厂的角色,ThreadPoolExecutor实现了Excutors接口。建议使用较为方便的 Executors 工厂方法 Executors.newCachedThreadPool()(*线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和 Executors.newSingleThreadExecutor()(单个后台线程)

二.核心线程池的内部实现

ThreadPoolExecutor类的构造函数

ThreadPoolExecutor(int  corePoolSize, int  maximumPoolSize, long   keepAliveTime, TimeUnit  unit, BlockingQueue<Runnable>   workQueue, ThreadFactory threadFactory ,RejectedExecutionHandler  handler)

coolPoolSize:

指定了线程池中线程的数量。

maximumPoolSize:

指定线程池中最大的线程数量

keepAliveTime:

超过coolPoolSize数量的线程在多长时间内会被销毁

unit:

keepAliveTime时间单位

workQueue:

任务队列,被提交尚未被执行的任务
他是一个BlockingQuene接口的对象,仅用于存放Runnable对象,根据队列任务分类可以使用以下几种BlockingQuene

1.直接提交的队列:
SynchronousQuene是一个特殊的BlockingQuene,SynchronousQuene没有容量,每一个插入操作都需要等待相对应的删除操作,反之,每一个删除操作都需要有一个对应的插入操作。如果使用SynchronousQuene,提交的任务不会被真实保存,而总是将新任务提交给线程执行。如果没有空闲的进程,则尝试创建新的进程,如果进程数量达到最大值,则执行拒绝策略。因此使用SynchronousQuene通常要设置很大的maximumPoolSize,否则很容易执行拒绝策略。

2.有界的任务队列:
ArrayBlockingQuene的构造函数必须带一个容量参数,表示该队列的最大容量

public ArrayBlockingQuene (int capcity)

当使用有界队列时,若有新的任务需要执行,如果线程池的实际线程数小于coolPoolSize,则会创建新的线程。若大于coolPoolSize,则会将新任务加入等待队列。当等待队列已满无法加入,则在总线程数不大于maximumPoolSize的前提下,创建新的进程执行任务。若大于maximumPoolSize则执行拒绝。可见,有界队列仅仅在任务队列满时,才可能将线程数提升到coolPoolSize以上。

3.*的任务队列:
LinkedBlockingQuene不存在任务入队失败的情况。当系统的线程数达到coolPoolSize后,就不会继续增加,若后续仍有新的任务加入,而又没有空闲的线程资源,则直接进入任务队列等待。若创建任务和处理任务的速度差异很大,*队列会保持快速增长,直到耗尽内存。

4.优先任务队列:
PriorityBlockingQuene是一个特殊的*队列,可以根据自身任务的优先级顺序先后执行,而LinkedBlockingQuene和ArrayBlockingQuene都是按照先进先出的方式处理的。

threadFactory:

线程工厂,用于创建线程

handler:

拒绝策略,当任务太多来不及处理如何拒绝

当线程池中的线程已经用完了,等待队列也已经排满了,这时可能需要使用拒绝策略。JDK内置四种拒绝策略
1.AbortPolicy策略: 直接抛出异常,阻止系统正常工作
2.CallerRunsPolicy 策略:只要线程未关闭,该策略直接在调用者线程中运行当前被丢弃的任务
3.DiscardOldestPolicy策略:丢弃最老的一个请求,也就是即将被执行的下一个任务,并尝试再次提交当前任务
4.DicardPolicy策略:默默丢弃无法处理的任务,不予任何处理