目录
1、newCachedThreadPool
2、newFixedThreadPool(fixed:固定的)
3、newSingleThreadExecutor
4、newScheduledThreadPool
5、newSingleThreadScheduledExecutor
6、newWorkStealingPool
7、为什么不推荐使用内置线程池?
以下是使用 Executors框架去创建的线程池,是Java内置的线程池,已经设好了相应的参数。
用法:ExecutorService executorService = Executors.newFixedThreadPool(2);
通过Executor框架对线程池的构建,都是基于 ThreadpoolExecutor 构造方法来构建的。
1、newCachedThreadPool
作用:创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的 ThreadFactory 创建新线程。整个线程池的线程数量并不固定,会随着需要来进行调整变化。该线程池使用同步队列 SynchronousQueue,该队列没有容量。
特征:
(1)线程池中数量没有核心线程,并且非核心线程数量不固定,没有上限,可达到最大值(Interger. MAX_VALUE)。
(2)线程池中的线程可进行缓存重复利用和回收(回收默认时间为1分钟,线程空闲时间超过60秒就会被回收)
(3)当线程池中,没有可用线程,会重新创建一个线程(会维持至少有一个空闲线程在线程池中)
(4)使用的是SynchronousQueue队列,只能存储一个任务
(5)60秒后将空闲线程关闭后,当线程池内线程为0时会自动关闭线程池
(6)初始化线程大小为0
创建方式:
(1)Executors.newCachedThreadPool();
(2)Executors.newCachedThreadPool(ThreadFactory threadFactory);
源码:
public static ExecutorService newCachedThreadPool() {/** 0:表示核心线程数为0* Integer.MAX_VALUE:表示最大线程数为Integer.MAX_VALUE* 60L:表示线程空闲时间为60秒,当线程数量超过核心线程数时,超过的空闲线程如果空闲时间超过60秒就会被回收,因为这个线程池的核心线程数量设置的为0,所以这个线程池中所有的线程只要是空闲时间超过了60s,都会被回收* TimeUnit.SECONDS:表示秒为时间单位* new SynchronousQueue<Runnable>():表示线程池中使用的阻塞队列为同步队列 */return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {/** 0:表示核心线程数为0* Integer.MAX_VALUE:表示最大线程数为Integer.MAX_VALUE* 60L:表示线程空闲时间为60秒* TimeUnit.SECONDS:表示秒为时间单位* new SynchronousQueue<Runnable>():表示线程池中使用的阻塞队列为同步队列* threadFactory:表示线程工厂*/return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),threadFactory);
}
执行流程:
1. 没有核心线程,直接向 SynchronousQueue 中提交任务。SynchronousQueue内部没有容器,该队列不会保留任务,任务达到后直接创建线程。
2. 如果有空闲线程,就去取出任务执行;如果没有空闲线程,就新建一个
3. 执行完任务的线程有 60 秒生存时间,如果在这个时间内可以接到新任务,就可以继续活下去,否则就被回收
适用场景:
- 只适合短暂的任务,如果是非常耗时的任务不建议使用,因为每次提交一个任务都会开启一个线程,线程过多可能导致OMM
- 用来创建一个可以无限扩大的线程池,适用于服务器负载较轻,执行很多短期异步任务。
2、newFixedThreadPool(fixed:固定的)
作用:创建一个可重用固定线程数的线程池,以无界队列方式来运行这些线程,该线程池的线程数在创建时就被固定了,不可以扩大。在任意时间点,在大多数线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在线程关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在,即使线程一直处在空闲状态且队列为空,也不会被回收,直到调用shutdown。该线程池使用无界队列 LinkedBlockingQueue,该队列的最大容量为 Integer.MAX_VALUE,可以认为是无界的。因为不可以扩大线程数,所以也就不存在回收线程。
特征:
(1)FixedThreadPool 的核心线程数和最大线程数都是指定值,也就是说当线程池中的线程数超过核心线程数后,任务都会被放到阻塞队列中。该线程池的核心线程数量和最大线程数量是一样的,即全部都为核心线程。
(2)FixedThreadPool选用的阻塞队列是 LinkedBlockingQueue,使用的是默认容量 Integer.MAX_VALUE, 相当于没有上限
(3)线程池中的线程处于一定的量,可以很好的控制线程的并发量
(4)线程可以重复被使用,在线程被显示关闭之前,都将一直存在
(5)初始化时该线程池中的线程数就定了,是固定的,不可以扩大
(6)当线程处于空闲,且任务队列为空时,线程池也不会关闭
创建方式:
(1)Executors.newFixedThreadPool(int nThreads);//nThreads为线程的数量
(2)Executors.newFixedThreadPool(int nThreads,ThreadFactory threadFactory);//nThreads为线程的数量,threadFactory创建线程的工厂方式
源码:
// 该线程池的最大线程数量和核心线程数量是一样的
public static ExecutorService newFixedThreadPool(int nThreads) {/*** nThreads:表示线程池中核心线程数量* nThreads:表示线程池中最大线程数量* 0L:表示线程池中空闲线程的存活时间,0表示当线程数量超过线程池的核心线程数后,超过的线程如果空闲时间超过0就会被回收,这也就说明该线程池的线程数量是不会超过nThreads的,超过的空闲线程就会被回收。但是如果没有超过核心线程数量,空闲线程就可以一直存活,除非显式地关闭线程* TimeUnit.MILLISECONDS:表示空闲线程的存活时间的单位* new LinkedBlockingQueue<Runnable>():表示线程池中的任务队列使用的是无界队列LinkedBlockingQueue*/return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}// 默认情况下该线程池地最大线程数量和核心线程数量都是1,说明该线程池中只有一个线程在工作
public static ExecutorService newSingleThreadExecutor() {/*** 1:表示线程池中核心线程数量* 1:表示线程池中最大线程数量* 0L:表示线程池中空闲线程的存活时间,0表示当线程数量超过线程池的核心线程数后,超过的线程如果空闲时间超过0就会被回收,这也就说明该线程池的线程数量是不会超过nThreads的,超过的空闲线程就会被回收。但是如果没有超过核心线程数量,空闲线程就可以一直存活,除非显式地关闭线程* TimeUnit.MILLISECONDS:表示空闲线程的存活时间的单位* new LinkedBlockingQueue<Runnable>():表示线程池中的任务队列使用的是无界队列LinkedBlockingQueue*/return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}
执行流程:
1. 线程数少于核心线程数,也就是设置的线程数时,新建线程执行任务
2. 线程数等于核心线程数后,将任务加入阻塞队列
3. 由于队列容量非常大,可以一直添加
4. 执行完任务的线程反复去队列中取任务执行
适用场景:
主要适用于固定大小的线程池,因为它是无界的阻塞队列,那么线程池中的线程不会扩大,适用与可以预测线程数的场景中,或者服务器的负载很高,为了资源的合理利用,需要对线程数量进行严格控制的场景中。
3、newSingleThreadExecutor
作用:创建一个使用单个 worker 线程的 线程池,即该线程池只会有一个线程处于活动状态,所以任务只能一个一个被执行。以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。该线程池使用无界队列 LinkedBlockingQueue,该队列的最大容量为 Integer.MAX_VALUE,可以认为是无界的。
特征:
(1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行
(2)该线程池是一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
(3)SingleThreadExecutor选用的阻塞队列是 LinkedBlockingQueue,使用的是默认容量 Integer.MAX_VALUE, 相当于没有上限
(4)newSingleThreadExecutor返回的是一个经过代理的ExecutorService,不能转换为ThreadPoolExecutor,这也就意味着它只有一些ExecutorService的基本方法
(5)SingleThreadExecutor与单独new出来的Thread区别在于,单独new出来的Thread任务结束之后线程也就会随着结束,而且不可以submit提交任务到队列
创建方式:
(1)Executors.newSingleThreadExecutor() ;
(2)Executors.newSingleThreadExecutor(ThreadFactory threadFactory);// threadFactory创建线程的工厂方式
源码:
public static ExecutorService newSingleThreadExecutor() {/*** 1(corePoolSize):表示线程池中的核心线程数量* 1:表示线程池的最大线程数量* 0L(keepAliveTime):表示线程池中超过corePoolSize数目的空闲线程最大存活时间,该线程池是值得核心线程数量为1,所以只要是线程池中的线程数量超过1,该空闲线程就会被回收* TimeUnit.MILLISECONDS:参数keepAliveTime的时间单位* new LinkedBlockingQueue<Runnable>():一个阻塞队列,用来存储等待执行的任务*/return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {/*** 1(corePoolSize):表示线程池中的核心线程数量* 1:表示线程池的最大线程数量* 0L(keepAliveTime):表示线程池中超过corePoolSize数目的空闲线程最大存活时间,该线程池是值得核心线程数量为1,所以只要是线程池中的线程数量超过1,该空闲线程就会被回收* TimeUnit.MILLISECONDS:参数keepAliveTime的时间单位* new LinkedBlockingQueue<Runnable>():一个阻塞队列,用来存储等待执行的任务* threadFactory:线程工厂,用来创建线程*/return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory));
}
适用场景:
适用于需要保证任务被按顺序执行,并且在任何时候都不会出现多个线程的情况。
4、newScheduledThreadPool
作用: 创建一个线程池,它可以实现在给定延迟后运行命令或者定期地执行。该线程池使用延迟阻塞队列 DelayedWorkQueue。
特征:
(1)线程池中具有指定数量的线程,即便是空线程也将保留
(2)可定时或者延迟执行线程活动
创建方式:
(1)Executors.newScheduledThreadPool(int corePoolSize);// corePoolSize线程的个数
(2)newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory);// corePoolSize线程的个数,threadFactory创建线程的工厂
源码:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {// corePoolSize:表示线程池中的核心线程数return new ScheduledThreadPoolExecutor(corePoolSize);
}public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) {// corePoolSize:表示线程池中的核心线程数// threadFactory:线程工厂,用于创建线程return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
5、newSingleThreadScheduledExecutor
作用: 创建一个单线程执行程序,它可实现在给定延迟后运行命令或者定期地执行。该线程池使用延迟阻塞队列 DelayedWorkQueue。
特征:
(1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行
(2)可定时或者延迟执行线程活动
创建方式:
(1)Executors.newSingleThreadScheduledExecutor() ;
(2)Executors.newSingleThreadScheduledExecutor(ThreadFactory threadFactory) ;//threadFactory创建线程的工厂
源码:
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));
}public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1, threadFactory));
}
6、newWorkStealingPool
特征:
(1)该方法会根据你的CPU核数来创建线程个数,也可指定线程数
(2)newWorkStealingPool其内部使用的是ForkJoinPool,在任务全部执行完之后该线程池会自动关闭
适用场景:
创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行
7、为什么不推荐使用内置线程池?
在《阿里巴巴 Java 开发手册》“并发处理”这一章节,明确指出线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
为什么呢?
- 使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源开销,解决资源不足的问题。如果不使用线程池,有可能会造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
另外,《阿里巴巴 Java 开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 构造函数的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
Executors 返回线程池对象的弊端如下(后文会详细介绍到):
- FixedThreadPool 和 SingleThreadExecutor:使用的是无界的 LinkedBlockingQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
- CachedThreadPool:使用的是同步队列 SynchronousQueue, 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。
- ScheduledThreadPool 和 SingleThreadScheduledExecutor : 使用的无界的延迟阻塞队列DelayedWorkQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
所以不建议用下面这些方法创建线程池,还是直接使用ThreadPoolExecutor的构造方法,自己制定相应的参数来创建。
相关文章:【线程池】Java的线程池
【线程池】Java线程池的核心参数