1、线程池优点:
- 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
2、常用的线程池
- newSingleThreadExecutor:创建一个
单线程化
的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。 - newFixedThreadPool:创建一个
指定工作线程数量
的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。 - newCachedThreadPool:创建一个
可缓存
线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
特点:- 工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
- 如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
- 在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统OOM。
- newScheduledThreadPool:创建一个
定长
的线程池,而且支持定时的
以及周期性
的任务执行,支持定时及周期性任务执行。
3、线程池七个核心参数
- corePoolSize : 核心线程大小。线程池一直运行,核心线程就不会停止。
- maximumPoolSize :线程池最大线程数量。非核心线程数量=maximumPoolSize-corePoolSize
- keepAliveTime :非核心线程的心跳时间。如果非核心线程在keepAliveTime内没有运行任务,非
核心线程会消亡。 - workQueue :阻塞任务队列。ArrayBlockingQueue,LinkedBlockingQueue等,用来存放线程任
务。 - defaultHandler :饱和策略。ThreadPoolExecutor类中一共有4种饱和策略。通过实现RejectedExecutionHandler接口。
- AbortPolicy : 线程任务丢弃报错。默认饱和策略。
- DiscardPolicy : 线程任务直接丢弃不报错。
- DiscardOldestPolicy : 将workQueue队首任务丢弃,将最新线程任务重新加入队列执行。
- CallerRunsPolicy :线程池之外的线程直接调用run方法执行。
- ThreadFactory :线程工厂。新建线程工厂。
4、线程池的执行流程
- 线程池执行
execute/submit方法
向线程池添加任务,当任务小于核心线程数
corePoolSize,线程池中可以创建新的线程,即使线程池中仍有空闲线程。 - 当任务大于
核心线程数
corePoolSize,就向阻塞队列
workQueue添加任务。 - 当
阻塞队列
workQueue满了,并且最大线程池大小
maximumPoolSize >核心线程数
corePoolSize时,新提交任务会创建新的线程执行任务。 - 当提交任务数超过
最大线程池大小
maximumPoolSize,就会执行饱和策略
。 - 当线程池中超过
核心线程数
corePoolSize线程,空闲时间
达到keepAliveTime时,关闭空闲线程。 - 当设置allowCoreThreadTimeOut(true)时,线程池中
核心线程数
corePoolSize线程空闲时间达到keepAliveTime也将关闭
5、execute()方法和submit()方法的区别
- execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功
与否。 - submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit) 方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
6、扩展
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。