Java并发之线程池
0. 序言
- 在Java中,使用线程来异步执行任务。Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建和销毁将消耗大量的计算资源。针对这种情况,我们需要使用线程池来管理线程,带来的好处有3个:
① 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
② 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
③ 提高线程的可管理性。线程是稀缺资源,不能无限制创建,否则不但会消耗资源,还会降低系统的稳定性,而使用线程池可以进行统一分配、调优和监控。而这些离不开对线程池原理的深入了解。 - 本篇文章会从线程池的分类、线程池的创建、向线程池提交任务、关闭线程池、配置线程池、线程池的监控、线程池的实现原理七个方面讲解线程池。
1. 线程池的分类
想知道线程池的分类,可以看看线程池工厂类Executors的静态方法,部分代码如下;
public static ExecutorService newFixedThreadPool(int var0) {
return new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
}
public static ExecutorService newSingleThreadExecutor() {
return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new Executors.DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));
}
public static ScheduledExecutorService newScheduledThreadPool(int var0) {
return new ScheduledThreadPoolExecutor(var0);
}
从以上代码可知:线程池分为5种,分别是FixedThreadPool、SingleThreadExecutor、CachedThreadPool、SingleThreadScheduledExecutor、ScheduledThreadPool。其中前3个线程池属于ThreadPoolExecutor类型,后2个线程池属于ScheduledThreadPoolExecutor类型。
2. 线程池的创建
从线程池的分类,我们得知线程池工厂类Executors创建了两种类型的线程池,分别是ThreadPoolExecutor类型和ScheduledThreadPoolExecutor类型。我们看下ScheduledThreadPoolExecutor的构造方法:
public ScheduledThreadPoolExecutor(int var1) {
super(var1, 2147483647, 0L, TimeUnit.NANOSECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue());
}
public ScheduledThreadPoolExecutor(int var1, ThreadFactory var2) {
super(var1, 2147483647, 0L, TimeUnit.NANOSECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue(), var2);
}
public ScheduledThreadPoolExecutor(int var1, RejectedExecutionHandler var2) {
super(var1, 2147483647, 0L, TimeUnit.NANOSECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue(), var2);
}
public ScheduledThreadPoolExecutor(int var1, ThreadFactory var2, RejectedExecutionHandler var3) {
super(var1, 2147483647, 0L, TimeUnit.NANOSECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue(), var2, var3);
}
从以上代码得知,ScheduledThreadPoolExecutor构造方法调用的是父类的构造方法,那它的父类是谁呢?
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService
原来ScheduledThreadPoolExecutor的父类是ThreadPoolExecutor,原来ScheduledThreadPoolExecutor的创建实际上是通过父类ThreadPoolExecutor来创建的,只是调用的构造方法中的参数不同,最明显的就是阻塞队列用的是DelayedWorkQueue。我们可以看到ThreadPoolExecutor是一个核心类,线程池的创建都离不开它,所以这里我们通过ThreadPoolExecutor创建一个线程池。这里只需要new一个ThreadPoolExecutor即可,不过在new之前,我们要先看下它的构造方法:
public ThreadPoolExecutor(int var1, int var2, long var3, TimeUnit var5, BlockingQueue<Runnable> var6) {
this(var1, var2, var3, var5, var6, Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int var1, int var2, long var3, TimeUnit var5, BlockingQueue<Runnable> var6, ThreadFactory var7) {
this(var1, var2, var3, var5, var6, var7, defaultHandler);
}
public ThreadPoolExecutor(int var1, int var2, long var3, TimeUnit var5, BlockingQueue<Runnable> var6, RejectedExecutionHandler var7) {
this(var1, var2, var3, var5, var6, Executors.defaultThreadFactory(), var7);
}
public ThreadPoolExecutor(int var1, int var2, long var3, TimeUnit var5, BlockingQueue<Runnable> var6, ThreadFactory var7, RejectedExecutionHandler var8) {
this.ctl = new AtomicInteger(ctlOf(-536870912, 0));
this.mainLock = new ReentrantLock();
this.workers = new HashSet();
this.termination = this.mainLock.newCondition();
if (var1 >= 0 && var2 > 0 && var2 >= var1 && var3 >= 0L) {
if (var6 != null && var7 != null && var8 != null) {
this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
this.corePoolSize = var1;
this.maximumPoolSize = var2;
this.workQueue = var6;
this.keepAliveTime = var5.toNanos(var3);
this.threadFactory = var7;
this.handler = var8;
} else {
throw new NullPointerException();
}
} else {
throw new IllegalArgumentException();
}