Java创建线程池的方式
Java创建线程池的方式
文章目录
一、通过Executors工厂方法创建
1.Executors.newSingleThreadExecutor()
解析:这是一个单线程来串行执行所有任务的线程池,保证只有一个线程工作,并且顺序执行任务。堆积任务存放在缓冲队列
构造方法:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}
参数解析:
- corePoolSize:
1
;核心线程数 - maximumPoolSize:
1
;最大线程数 - keepAliveTime:
0L
;空闲线程存活的时间 - TimeUnit:
TimeUnit.MILLISECONDS
(毫秒);线程存活时间的单位 - workQueue:
new LinkedBlockingQueue<Runnable>()
;基于链表实现的无界缓冲队列
代码实现:
public class SingleThreadExecutorDemo {
public static void main(String[] args) {
// 创建线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 顺序执行任务
for (int i = 0; i < 10; i++) {
int index = i;
threadPool.execute(() -> {
System.out.println(index + ": 任务被执行: " + Thread.currentThread().getName());
});
}
}
}
执行结果:
2.Executors.newFixedThreadPool(n)
解析:这是一个固定线程个数的线程池,只有核心线程。每次提交一个任务就创建一个线程,直到线程数达到线程池的最大大小,其他任务放在缓存队列等待执行。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。FixedThreadPool 多数针对一些很稳定很固定的正规并发线程,多用于服务器。
构造方法:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
参数解析:
- corePoolSize:
nThreads
;核心线程数 - maximumPoolSize:
nThreads
;最大线程数 - keepAliveTime:
0L
;空闲线程存活的时间 - TimeUnit:
TimeUnit.MILLISECONDS
(毫秒);线程存活时间的单位 - workQueue:
new LinkedBlockingQueue<Runnable>()
;基于链表实现的无界缓冲队列
代码实现:
public class FixedThreadPoolDemo {
public static void main(String[] args) {
// 创建 2 个线程的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
// 创建任务
Runnable runnable = () -> System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
// 线程池执行任务(一次添加 8 个任务)
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
threadPool.execute(runnable);
}
}
执行结果:
3.Executors.newCachedThreadPool()
解析:这是一个无界线程池,如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60 秒不执行任务)线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。SynchronousQueue 是一个是缓冲区为 1 的阻塞队列。缓存型池子通常用于执行一些生存期很短的异步型任务,因此在一些面向连接的 daemon 型 SERVER 中用得不多。但对于生存期短的异步任务,它是 Executor 的首选。
构造方法:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
参数解析:
- corePoolSize:
0
;核心线程数 - maximumPoolSize:
Integer.MAX_VALUE
;最大线程数 - keepAliveTime:
60L
;空闲线程存活的时间 - TimeUnit:
TimeUnit.SECONDS
(秒);线程存活时间的单位 - workQueue:
new SynchronousQueue<Runnable>()
;缓冲区为1的阻塞队列
代码实现:
public class CachedThreadPoolDemo {
public static void main(String[] args) {
// 创建线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
// 执行任务
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> {
System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
});
}
}
}
执行结果:
4. Executors.newScheduledThreadPool(n)
解析:这是个核心线程池固定,大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。创建一个周期性执行任务的线程池。如果闲置,非核心线程池会在 DEFAULT_KEEPALIVEMILLIS 时间内回收。
构造方法:
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
参数解析:
- corePoolSize:
corePoolSize
;核心线程数 - maximumPoolSize:
Integer.MAX_VALUE
;最大线程数 - keepAliveTime:
0L
;空闲线程存活的时间 - TimeUnit:
NANOSECONDS
(纳秒);线程存活时间的单位 - workQueue:
new DelayedWorkQueue())
;延时队列
代码实现:
public class ScheduledThreadPoolDemo {
public static void main(String[] args) {
// 创建线程池
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 5; i++){
// 添加定时执行任务(2s 后执行)
System.out.println("添加任务,时间:" + new Date());
threadPool.schedule(() -> {
System.out.println("任务被执行,时间:" + new Date());
}, 2, TimeUnit.SECONDS);
}
}
}
执行结果:
5. Executors.newSingleThreadScheduledExecutor()
解析:这是一个单线程的可以执行延迟任务的线程池。这种线程池可以看做是 ScheduledThreadPool 的单线程版本。
构造方法:
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
参数解析:
- corePoolSize:
1
;核心线程数 - maximumPoolSize:
Integer.MAX_VALUE
;最大线程数 - keepAliveTime:
0L
;空闲线程存活的时间 - TimeUnit:
NANOSECONDS
(纳秒);线程存活时间的单位 - workQueue:
new DelayedWorkQueue())
;延时队列
代码实现:
public class SingleThreadScheduledExecutorDemo {
public static void main(String[] args) {
// 创建线程池
ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
// 添加定时执行任务(2s 后执行)
System.out.println("添加任务,时间:" + new Date());
threadPool.schedule(() -> {
System.out.println("任务被执行,时间:" + new Date());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
}, 2, TimeUnit.SECONDS);
}
}
执行结果:
6.Executors.newWorkStealingPool():
解析:这是一个抢占式执行的线程池,执行任务的顺序不确定。需要注意的是此方法是 JDK 1.8 版本新增的,所以 1.8 版本之前的程序中不能使用。
构造方法:
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
参数解析:
-
parallelism:
Runtime.getRuntime().availableProcessors();
是获取当前系统可以的CPU核心数。(可以自定义) -
ForkJoinWorkerThreadFactory:
ForkJoinPool.defaultForkJoinWorkerThreadFactory
; 创建新线程的工厂对于默认值,请使用defaultForkJoinWorkerThreadFactory。 -
UncaughtExceptionHandler:
null
;由于执行任务时遇到不可恢复的错误而终止的内部工作线程的处理程序。对于默认值,请使用null -
boolean:
true
; 如果为true,则为从未联接的分叉任务建立本地先进先出调度模式。在工作线程仅处理事件样式异步任务的应用程序中,此模式可能比默认的基于本地堆栈的模式更合适。对于默认值,请使用false。
代码实现:
public class WorkStealingPoolDemo {
public static void main(String[] args) {
// 创建线程池
ExecutorService threadPool = Executors.newWorkStealingPool();
// 执行任务
for (int i = 0; i < 10; i++) {
final int index = i;
threadPool.execute(() -> {
System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
});
}
// 确保任务执行完成
while (!threadPool.isTerminated()) {
}
}
}
执行结果:
二、通过Executors工厂创建
1.ThreadPoolExecutor()
解析: 这是最原始,也是最推荐的手动创建线程池的方法。创建时支持自定义某些属性,比如核心线程数、最大线程数等。
构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
参数解析:
- corePoolSize:
corePoolSize
;核心线程数 - maximumPoolSize:
maximumPoolSize
;最大线程数 - keepAliveTime:
keepAliveTime
;空闲线程存活的时间 - TimeUnit:
unit
;空闲线程存活时间的单位 - BlockingQueue:
workQueue
;线程池所用的队列类型 - ThreadFactory:
threadFactory
线程创建工厂 - RejectedExecutionHandler:
handler
corePoolSize已满,队列已满,maxPoolSize 已满,最后的拒绝策略。根据拒绝策略拒绝任务
代码实现:
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
// 执行任务
for (int i = 0; i < 10; i++) {
final int index = i;
threadPool.execute(() -> {
System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
});
}
}
}
执行结果
三、总结
线程池工作核心流程:
1、先判读当前线程数是否小于 corePoolSize 。如果小于就新建线程执行
2、如果大于,就判断阻塞队列是否已经满了,如果没有满,就加入阻塞队列中。
3、如果满了,就判断当前线程数是否小于 maxPoolSize 。如果小于,就直接创建线程执行。如果大于就根据拒绝策略,拒绝任务。
拒绝策略
线程池有四种拒绝策略
1、丢弃任务,并抛出异常
2、丢弃任务,单不抛出异常
3、丢弃队列最前面的任务,然后重新提交当前任务。
4、线程池什么都不做,由当前线程自己处理。
线程池的关闭:
在调用shutdown() 或者shutdownNow() 之后,线程池并不会立即关闭,会等待所有任务执行完成之后,才会关闭线程池。
shutdown() 不会清空任务队列,并且只会中断空闲线程。
shutdownNow() 会清空任务队列,并且中断所有的线程。