什么是线程池
线程池是指在初始化一个多线程应用过程中创建的一个线程集合,该线程集合使得程序在执行任务时不再需要临时创建新的线程,而是可以直接调用线程池中的线程。线程池可以同时执行多个任务,如果任务队列已满,则新来的任务会排队等待,线程池的线程数量不会大于既定的最大值。
线程池的优点
- 降低资源的消耗。重复利用已创建的线程,降低创建和销毁线程的资源消耗
- 提高程序的响应效率。任务到达时,不再需要等待创建线程时间,直接调用即可。
- 提高线程的可管理性
线程池的参数
- corePoolSize:核心池的大小;在创建线程池后,默认情况下,线程池中没有任何线程池,而是等到任务到达时才创建线程去执行任务。除非调用prestartAllCoresThreads()或者prestartCoreThread()这两个方法预创建线程;即在没有任务到来之前就创建了corePoolSize个线程。当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
- maximumPoolSize:最大线程数。表示线程池中最多能创建的线程数。
- keepAliveTime:非核心线程闲置时的超时时长,超过这个时长会被回收,如果核心线程.allowCoreThreadTimeOut设置成了true,那么核心线程在闲置的情况下,也会被回收。
- unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
TimeUnit.DAYS //天 TimeUnit.HOURS //小时 TimeUnit.MINUTES //分钟 TimeUnit.SECONDS //秒 TimeUnit.MILLISECONDS //毫秒 ,等于1∗10−3s TimeUnit.MICROSECONDS //微秒,等于1∗10−6s TimeUnit.NANOSECONDS //纳秒,等于1∗10−9s
- workQueue:线程池中的任务队列,通过execute方法提交的runnable对象会存储在该队列中
- allowCoreThreadTimeout:允许核心线程超时
下面说说系统常用的4种线程池的实现原理:
1.newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务都是按照指定的顺序(FIFO,LIFO,优先级)执行。
2.newFixedThreadPool()
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3.newScheduledThreadPool(int corePoolSize)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue());
}
固定核心线程,主要用于定时任务或者周期性任务。
4.newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
没有核心线程,不固定线程的数量,超时时长60秒,超过后会被回收,所以它的特性在空闲时,没有线程几乎是不占用内存的,适用于多量耗时少的任务。
以上4种都是通过Executors 下面对应类型的方法来创建。
线程池饱和策略
1. Abort策略:默认策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try catch,否则程序会直接退出。
2. CallerRuns策略:为调节机制,既不抛弃任务也不抛出异常,而是将某些任务回退到调用者。不会在线程池的线程中执行新的任务,而是在调用exector的线程中运行新的任务。
3. Discard策略:新提交的任务被抛弃,任务不执行。
4. DiscardOldest策略:当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务(抛弃下一个将被执行的任务),然后将被拒绝的任务添加到等待队列中,如果队列是一个优先队列,那么抛弃 最旧的策略就会抛弃优先级最高的任务,因此不要将两者在一起使用。
5. 用户自定义拒绝策略(最常用):实现RejectedExecutionHandler,并自己定义策略模式
/**
* 循环,当队列有空位时,该任务进入队列,等待线程池处理
*/
public class TestRejectedExecutionHandler implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}