开发过程中经常要用线程池,今天就好好整理一下java中线程池的知识。
定义
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。
本篇文章涉及的源码来自jdk1.8。
直接到Executors类中把返回值为ExecutorService类型的静态函数都找了出来,有以下这几个
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
ExecutorService workStealingPool = Executors.newWorkStealingPool();
各个线程池都是啥?一个一个看源码开始分析
CachedThreadPool(可缓存线程池)
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
CachedThreadPool的线程池参数:
- 核心线程数为0
- 最大线程数量为 Integer.MAX_VALUE
- 线程超时时间为60秒
- 任务队列是SynchronousQueue,SynchronousQueue是一个无容量的队列,就是不能装任务的。
其实,也能理解CachedThreadPool为什么是一个可缓存线程池了。从线程池参数就可以看到,只要一有任务来就马上用空闲的线程或者创建新的线程去执行任务,每一个线程执行完任务之后就被缓存60秒,在60秒内没有再利用的话就被销毁。
FixedThreadPool (一个指定工作线程数量的线程池)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
FixedThreadPool 的线程池参数:
- 核心线程数为nThreads
- 最大线程数量为 nThreads
- 线程超时时间为0ms
- 任务队列是没有指定容量的LinkedBlockingQueue
FixedThreadPool 中的核心线程数和最大线程数量相同。当工作的线程数量等于核心线程数量的时候,再来任务的话,不会再创建新的线程的了,而是把任务放在任务队列LinkedBlockingQueue中。很好理解CachedThreadPool是一个指定工作线程数量的线程池。但是CachedThreadPool的缺陷也显现出来了:因为它的最大线程数量等于核心线程数量,所以即使线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
SingleThreadExecutor(一个单线程化的Executor)
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
SingleThreadExecutor的线程池参数:
- 核心线程数为1
- 最大线程数量为 1
- 线程超时时间为0ms
- 任务队列是没有指定容量的LinkedBlockingQueue
SingleThreadExecutor的核心线程数量和最大线程数量都是1。所以SingleThreadExecutor是一个一个单线程化的Executor。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。
singleThreadScheduledExecutor (一个单线程可定时执行任务的线程池)
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
看一下ScheduledThreadPoolExecutor
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService {
//已经省略掉其他代码
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
}
ScheduledThreadPoolExecutor的线程池参数:
- 核心线程数为1
- 最大线程数量为 Integer.MAX_VALUE
- 线程超时时间为 10ms
- 任务队列是没有指定容量的DelayedWorkQueue
从字面意思上看,ScheduledThreadPoolExecutor是一个可安排定时任务的线程池。那为什么singleThreadScheduledExecutor 是一个单线程可定时执行任务的线程池呢?
看一下DelegatedScheduledExecutorService类源码
private static class DelegatedScheduledExecutorService
extends DelegatedExecutorService
implements ScheduledExecutorService {
private final ScheduledExecutorService e;
DelegatedScheduledExecutorService(ScheduledExecutorService executor) {
super(executor);
e = executor;
}
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
return e.schedule(command, delay, unit);
}
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
return e.schedule(callable, delay, unit);
}
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
return e.scheduleAtFixedRate(command, initialDelay, period, unit);
}
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
return e.scheduleWithFixedDelay(command, initialDelay, delay, unit);
}
}
没搞懂,为什么singleThreadScheduledExecutor 这个线程池的“单线程“的代码体现在哪里?打个标记,后续解决了回来继续更新//TODO
workStealingPool(工作训练线程池)
public static ExecutorService newWorkStealingPool() {
//第一个参数是cpu核心数量,第二个参数是一个线程工厂
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
看下ForkJoinPool 这个类
public class ForkJoinPool extends AbstractExecutorService {
//省略了其他代码...
public ForkJoinPool(int parallelism,
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
boolean asyncMode) {
//asyncMode为true说明是先进先出,false则是先进后出
this(checkParallelism(parallelism),
checkFactory(factory),
handler,
asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
"ForkJoinPool-" + nextPoolId() + "-worker-");
checkPermission();
}
}
看一下checkPermission
/**
* If there is a security manager, makes sure caller has
* permission to modify threads.
*/
private static void checkPermission() {
SecurityManager security = System.getSecurityManager();
if (security != null)
security.checkPermission(modifyThreadPermission);
}
这个函数应该是用来检测是否有权限修改线程的?不是很懂,有点点懵。先打个标记//TODO,后续搞明白了会回来更新
目前对workStealingPool的源码还是十分的陌生的,先学习足够了再回来继续写//TODO