在了解线程池之前,要先理解一个问题
为什么要使用线程池
a.线程池可以更加有效的利用cpu资源,减少创建线程,销毁线程带来的多余的开销
b.线程池能控制线程的并发数目,管理线程并发问题
c.线程池用一些其他的特性,延时调度执行,例如:scheduleThreadPool能使线程定时调度执行
我们先看下线程池的构造方法:
int corePoolSize => 该线程池中核心线程数
核心线程指的是在线程池中不会被回收的线程,当然也可以通过设置allowCoreThreadTimeOut 为true将超过keepAliveTime空闲的核心线程销毁。
int maximumPoolSize => 最大线程数
最大线程是指核心线程和非核心线程的最大容量,即线程池的最大值
long keepAliveTime => 闲置存活时间
一般是指非核心线程的空闲存活时间,但是allowCoreThreadTimeOut 为true时也适用于核心线程
TimeUnit unit =>闲置存活时间的单位(枚举类型)
NANOSECONDS : 1微毫秒 = 1微秒 / 1000,MICROSECONDS : 1微秒 = 1毫秒 / 1000,MILLISECONDS : 1毫秒 = 1秒 /1000,SECONDS : 秒,MINUTES : 分,HOURS : 小时,DAYS : 天
BlockingQueue<Runnable> workQueue =>线程池的任务队列
1.1 直接提交的任务队列(SynchronousQueue)
(1) SynchronousQueue没有容量。
(2) 提交的任务不会被真实的保存在队列中,而总是将新任务提交给线程执行。如果没有空闲的线程,则尝试创建新的线程。如果线程数大于最大值maximumPoolSize,则执行拒绝策略。
1.2 有界的任务队列(ArrayBlockingQueue)
(1) 创建队列时,指定队列的最大容量。
(2) 若有新的任务要执行,如果线程池中的线程数小于corePoolSize,则会优先创建新的线程。若大于corePoolSize,则会将新任务加入到等待队列中。
(3) 若等待队列已满,无法加入。如果总线程数不大于线程数最大值maximumPoolSize,则创建新的线程执行任务。若大于maximumPoolSize,则执行拒绝策略。
1.3 无界的任务队列(LinkedBlockingQueue)
(1) 与有界队列相比,除非系统资源耗尽,否则不存在任务入队失败的情况。
(2) 若有新的任务要执行,如果线程池中的线程数小于corePoolSize,线程池会创建新的线程。若大于corePoolSize,此时又没有空闲的线程资源,则任务直接进入等待队列。
(3) 当线程池中的线程数达到corePoolSize后,线程池不会创建新的线程。
(4) 若任务创建和处理的速度差异很大,无界队列将保持快速增长,直到耗尽系统内存。
(5) 使用无界队列将导致在所有 corePoolSize 线程都忙时,新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize(因此,maximumPoolSize 的值也就无效了)。当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
1.4 优先任务队列(PriorityBlockingQueue)
(1) 带有执行优先级的队列。是一个特殊的无界队列。
(2) ArrayBlockingQueue和LinkedBlockingQueue都是按照先进先出算法来处理任务。而PriorityBlockingQueue可根据任务自身的优先级顺序先后执行(总是确保高优先级的任务先执行)。
1.5 延迟队列(DelayQueue)
队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务
ThreadFactory =>线程工厂用来产生线程
就是一个接口,newThread方法就是用来生产线程的,子类需要实现这个方法来根据自己规则生产相应的线程。
RejectedExecutionHandler handler =>拒绝策略
当线程池线程大于最大线程容量时,执行拒绝策略,目前
- AbortPolicy
- 丢掉这个任务并且抛出RejectedExecutionException异常
- DiscardPolicy
- 会直接丢掉这个任务并且不会有任何异常
- DiscardOldestPolicy
- 会将最早进入队列的任务删掉腾出空间,再尝试加入队列
- CallerRunsPolicy
- 如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行
- 自定义
- 自己定义一个拒绝策略,只要实现RejectedExecutionHandler接口,并且实现rejectedExecution方法就可以了
ThreadPoolExecutor的策略
线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务
线程数量达到了corePools,则将任务移入队列等待
队列已满,新建线程(非核心线程)执行任务
队列已满,总线程数又达到了maximumPoolSize,就会由上面那位星期天(RejectedExecutionHandler)抛出异常
常见四种线程池
CachedThreadPool()
使用 直接提交的任务队列(SynchronousQueue)
可缓存线程池:
- 线程数无限制
- 有空闲线程则复用空闲线程,若无空闲线程则新建线程
- 一定程序减少频繁创建/销毁线程,减少系统开销
创建方法:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
源码:
FixedThreadPool()
使用 无界的任务队列(LinkedBlockingQueue)
定长线程池:
- 可控制线程最大并发数(同时执行的线程数)
- 超出的线程会在队列中等待
创建方法:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads);
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory);
源码:
ScheduledThreadPool()
使用 延迟队列(DelayQueue)
定长线程池:
- 支持定时及周期性任务执行。
创建方法:
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);
源码:
SingleThreadExecutor()
使用 无界的任务队列(LinkedBlockingQueue)
单线程化的线程池:
- 有且仅有一个工作线程执行任务
- 所有任务按照指定顺序执行,即遵循队列的入队出队规则
创建方法:
ExecutorService singleThreadPool = Executors.newSingleThreadPool();
源码:
线程池常问的一些问题:
ExecutorService .execute和ExecutorService.submit方法有什么区别 executer提交任务但是不会返回结果,不会返回异常,submit 会返回结果和异常