参考:
https://www.jianshu.com/p/7b2da1d94b42
https://www.jianshu.com/p/c41e942bcd64
https://blog.csdn.net/u013651026/article/details/77334004
https://blog.csdn.net/wfzczangpeng/article/details/52015866
如果app运行中开启了大量的线程,很容易造成系统卡顿等问题,这个时候就可以使用线程池。
java线程池:ThreadPoolExecutor--->AbstractExecutorService--->ExecutorService
既可以使用ThreadPoolExecutor类new一个自定义的线程池,也可以通过Executors类创建指定类型的线程池。
一,常用线程池介绍
----------------------------------------------------------------------------------------------------------------------------------
1,CachedThreadPool,缓存型线程池,调用Executors.newCachedThreadPool()创建。特点是没有核心线程,只有非核心线程,并且每个非核心线程空闲等待的时间为60s,采用SynchronousQueue队列。使用时查看池中是否已创建并且还没超时(等待时间60秒)的线程,有就重用没有再新建。缓存型池子通常用于执行一些生存期很短的异步型任务。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
分析:
(1)没有核心线程,全为非核心线程,SynchronousQueue不存储元素,每次插入操作就必须移除一个操作,一个移除操作也会插入一个操作。
(2)当一个线程任务(Runnable)插入到线程池里运行时,SynchronousQueue调用offer方法提交任务,如果线程池中有空闲线程,如果线程池中有线程处于空闲状态,调用SynchronousQueue的poll方法来移除任务并交给线程处理,如果没有空闲线程则开启一个新的非核心线程来处理。
(3)参数maximumPoolSize的值为Integer.MAX_VALUE,所以线程池长度无界,如果提交任务(Runnable)的速度比处理任务的速度快,线程池不断创建新的线程,容易造成系统资源紧张,此时需要采取措施调整提交和处理任务的速度。
(4)CachedThreadPool适用于有大量需要立即执行的耗时少的任务的情况。
----------------------------------------------------------------------------------------------------------------------------------
2,FixedThreadPool,可重用固定线程数线程池,调用Executors.newFixedThreadPool(int nThreads)创建。特点是参数为核心线程数,只有核心线程,无非核心线程,并且阻塞队列无界。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
分析:
(1)任何时间都只有固定数量的线程Thread在运行,且keepAliveTime为0表示无超时时间所以运行的Thread会一直存在不被销毁。
(2)LinkedBlockingQueue是阻塞型链表队列,当有新的Runnable任务提交进来而线程池中的运行Thread正在处理任务时,新的Runnable放入队列,等待有运行Thread处于空闲时被队列移出交给Thread处理。
(3)FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器。
----------------------------------------------------------------------------------------------------------------------------------
3,ScheduledThreadPool,定时延时执行线程池,需要确定核心线程数,非核心线程数无界,线程池默认每隔10毫秒创建Thread执行提交的任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
用法:
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
适合多个需要依次执行的线程任务。
----------------------------------------------------------------------------------------------------------------------------------
4,SingleThreadPool,单核依序执行线程池,只有一个运行Thread,新插入队列的任务Runnable必须等待上一个任务被运行Thread执行完后才能被队列取出交给Thread执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
适合需要一个个顺序执行的任务。
----------------------------------------------------------------------------------------------------------------------------------
二,定制自己需求的线程池
上面4个类型的线程池都出自ThreadPoolExecutor,通过改变对应参数的值实现不同的效果。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
...
}
参数说明:
1,corePoolSize,线程池中核心线程的数量,线程池中的核心线程不会被系统轻易回收
2,maximumPoolSize,线程池中最大运行线程数量
3,keepAliveTime,非核心线程的超时时长,非核心线程闲置时间超时则被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则核心线程也会在超时后被回收
4,unit,TimeUnit类型,超时时长单位,毫秒,秒,分,时,天等
核心和非核心线程:核心线程一旦创建会一直执行任务或等待任务到来,非核心线程在运行线程(Thread)队列都塞满了执行任务(Runnable)时会创建一个闲置超时会被回收的运行线程去执行任务队列里等待被执行的任务。当通过execute(Runnable)方法提交新的Runnable进任务队列里,如果当前运行线程(Thread)数量消息设定的核心线程数量,哪怕当前运行线程中有闲置的线程,也会新建一个运行线程。
5,workQueue,任务队列,存储提交过来的Runnable,在ThreadPoolExecutor线程池的API文档中,一共推荐了三种等待队列,它们是:SynchronousQueue、LinkedBlockingQueue和ArrayBlockingQueue。队列分为有限队列和无限队列。
(1)有限队列:
SynchronousQueue,同步阻塞队列,通过put方法插入任务(Runnable)到队列里,等待直到同步阻塞队列调用take取出这个Runnable执行,需要将maximumPoolSize设置为Integer.MAX_VALUE以防止无法正常插入任务。注意,当任务持续以平均提交速度大余平均处理速度时,会导致队列无限增长问题。
ArrayBlockingQueue,数组阻塞队列,按照先进先出原则对插入任务排序,新插入的任务放到队列尾部,从队列头部执行取出操作,此队列有数组长度限制,当新插入的任务超出队列长度时会引起操作阻塞,另外从空队列中取出任务也将引起操作阻塞。
(2)无限队列:
LinkedBlockingQueue,链表阻塞队列,当不指定队列长度时,队列长度默认为Integer.MAX_VALUE,如果设置了队列长度,当新插入的任务超出队列长度时会引起操作阻塞。LinkedBlockingQueue只能将任务插入到队列尾部,从队列头部取出任务。
LinkedBlockingDeque,链表阻塞双端队列,LinkedBlockingDeque既可以从尾部插入/取出元素,还可以从头部插入元素/取出元素。
PriorityBlockingQueue,是一个按照优先级进行内部元素排序的无限队列。存放在PriorityBlockingQueue中的元素必须实现Comparable接口,这样才能通过实现compareTo()方法进行排序。
LinkedTransferQueue,也是一个无限队列,它除了具有一般队列的操作特性外(先进先出),还具有一个阻塞特性:LinkedTransferQueue可以由一对生产者/消费者线程进行操作,当消费者将一个新的元素插入队列后,消费者线程将会一直等待,直到某一个消费者线程将这个元素取走,反之亦然。
6,threadFactory,线程工厂,用于创建执行线程,如果用户不需自定可直接调用Executors.defaultThreadFactory()使用默认的线程工厂,如有需要自定则可以自己创建实体。
public class MyFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
}
7,handler,拒绝执行策略,当一个新任务Runnable提交给线程池,线程池里的核心线程无法处理,又或者线程池最大线程数已满而无法加入任务队列,非核心也无法创建执行此Runnable,或者调用shutdown停止线程池工作,总之线程池无法正常处理这个Runnable时,线程池会拒绝这个Runnable,并交给拒绝策略RejectedExecutionHandler执行指定的拒绝操作。
如果用户不指定拒绝策略,系统会自动调用AbortPolicy类型的拒绝策略。
线程池中四种可直接使用的拒绝策略:
(1)CallerRunsPolicy:在rejectedExecution方法内直接调用Runnable的run方法。源码:
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
(2)AbortPolicy:Runnable任务被拒绝后直接抛出RejectedExecutionException异常,无其他操作。源码:
public static class AbortPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
(3)DiscardPolicy:不执行Runnable的run方法,也不抛出异常,不做任何处理。源码:
public static class DiscardPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
(4)DiscardOldestPolicy:检查当前线程池的任务等待队列,调用队列的poll方法强行取出,视图将被拒绝的Runnable提交到线程池中执行。源码:
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
----------------------------------------------------------------------------------------------------------------------------------
三,常用方法:
execute / submit:提交Runnable到线程池,execute无返回值,submit有返回值。
shutDown:关闭线程池,未执行的任务会被清除,正在执行的任务不受影响。
shutDownNow:关闭线程池,尝试终止正在执行的任务,正常执行的任务容易抛出异常。
allowCoreThreadTimeOut:是否运行闲置的核心线程超时后被回收
beforeExecute:任务执行前执行的方法,被protected修饰,无法直接使用
afterExecute:任务执行结束后执行的方法,被protected修饰,无法直接使用
terminated:线程池关闭后执行的方法,被protected修饰,无法直接使用