首先,我们先看一下阿里的规范
阿里发布的 Java开发手册中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险
说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor: 主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool: 主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
3)自定义创建线程池,可以根据我们开发的不通的需求,设置不同的规则,比如阻塞队列、拒绝策略等
Executors 的方法
newFixedThreadPool:该方法返回一个固定数量的线程池,线程数不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中,等待有空闲的线程去执行。
newSingleThreadExecutor: 创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中。
newCachedThreadPool:返回一个可根据实际情况调整线程个数的线程池,不限制最大线程数量,若用空闲的线程则执行任务,若无任务则不创建线程。并且每一个空闲线程会在 60 秒后自动回收
newScheduledThreadPool: 创建一个可以指定线程的数量的线程池,但是这个线程池还带有延迟和周期性执行任务的功能,类似定时器
public static ExecutorService newCachedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
单单仅仅只是这段话,我们可能不明白为啥要这么做,就让我们开始学习ThreadPoolExecutor
ThreadPoolExecutor的构造方法
/**
* ==========阿里规范使用ThreadPoolExecutor创建线程池==========
* corePoolSize 核心线程设置0的好处就是当我们没有任务,就不需要维持线程池,所有任务都放到队列中,然后创建线程去执行我们的任务
* maximumPoolSize-->POOL_SIZE 最大100个线程执行任务
* keepAliveTime + TimeUnit.SECONDS-->失效时间
* workQueue队列
* LinkedBlockingQueue-->阻塞队列默认容量为Integer的最大值,此队列容易内存溢出
* ArrayBlockingQueue-->固定容量,数组容易越界
* SynchronousQueue -->同步队列,意思就是每来一个任务创建一个线程,当线程超过maximumPoolSize就会抛异常RejectedExecutionException
* threadFactory-->线程池工厂类负责创建线程
*
* RejectedExecutionHandler defaultHandler-->拒绝策略 线程数量大于等于我们的线程池时的策略
* AbortPolicy-->默认的拒绝策略 抛出RejectedExecutionException异常的方式拒绝任务。这也就是SynchronousQueue容易抛异常的原因
* DiscardPolicy -->什么都不做,直接丢弃任务
* DiscardOldestPolicy -->把队列中最老的任务(执行时间最长的任务)拿出来扔掉
* CallerRunsPolicy-->在任务提交的线程把任务给执行了
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
2、为什么使用自定义线程池
-
相对于之前的Excutor的工厂类,自定义线程池可以根据我们自己业务需求来定义阻塞队列和拒绝策略 就阿里的规范也让我们使用ThreadPoolExcutor来创建线程池
-
newFixedThreadPool和newSingleThreadExecutor其实使用的队列是LinkedBlockingQueue 虽然可以在很多场景使用,但是这会有一个问题,就是无限堆积队列的时候,就会OOM特别newSingleThreadExecutor 看其构造方法核心线程和最大线程只有一个,也就是说后面来的任务都会放到LinkedBlockingQueue里面,如果正在执行的任务就会堵塞
-
newScheduledThreadPool 看其构造方法最大线程数为Integer最大值,线程数到达一定数量,肯定也会OOM,那这样设置线程数的目的是什么,这么大的线程数为什么队列的容量只有16,这有什么意义
-
newCachedThreadPool使用的队列是同步队列,最大线程数设置为INTEGER的最大值,核心线程设置为0,这个线程的意思就是,来一个任务创建一个线程来执行,假如一次来了100W个任务,开100W个线程?然后OOM?
-
综合这几种,自定义线程池就有必要了,因为我们根据不同的业务逻辑或不同的业务场景来设置不同的线程池,也不用浪费我们的系统资源,毕竟线程的创建也是耗费资源
3、自定义线程拒绝策略
//一个设计模式,责任链模式,根据我们定义的线程的策略,来做不同的处理
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
/**
* AbortPolicy 默认的 直接抛异常RejectedExecutionException,丢失当前任务
*/
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
/**
* CallerRunsPolicy策略,判断当前线程是否在执行任务,如果没有,那么就当前线程来执行掉这个任务
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
/**
* DiscardOldestPolicy策略,判断当前线程是不是在执行任务,如果没有,吧队列中等待最久的一个任务拿出来,在吧当前任务放进去
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
/**
* DiscardPolicy策略,我们可以看到,直接丢弃任务,什么都不管
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
态度决定未来,坚持就是胜利,码出人生