概述
在上一篇文章中介绍了 JDK 中提供的线程池类 ThreadPoolExecutor 以及线程池的参数,在实际使用中需要了解个参数的含义从而才能正确的使用线程池来达到我们的目的;
鉴于此 JDK 也给我们提供了几个可以开箱即用的默认线程池的实现,使用 JDK 的工具类Executors 操作即可;
下面将介绍几个线程池的区别以及使用场景;
具体实现类介绍
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
结合上面的源码及上一篇关于线程池参数的介绍,基本上就可以知道 newFixedThreadPool 实现的线程池就和它的方法名一样是一个固定大小的线程池;
固定是针对线程池中线程的数量而言的,可以看到这个线程池是一个核心线程数和最大线程数都一样的线程池,相应的超时时间也是设置的0,这里的阻塞队列用的是无参的 LinkedBlockingQueue 因此是一个最大限制是 int 的最大值的队列,基本上可以认为是一个无界的队列了;
具体使用也非常简单用 Executors.newFixedThreadPool(num) 就可以使用了,但是这个线程需要注意的是:
1、它创建的线程数是固定,因此设置的数量不能太大,太大会浪费资源;
2、这个线程池的队列是一个无界的,因此如果任务积压太多会导致队列无限增长,可能会引起 OOM 异常;
3、理论上应该不会触发拒绝策略,这里的拒绝策略是线程池默认的,因此会抛出异常;
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
static class FinalizableDelegatedExecutorService
extends DelegatedExecutorService {
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
protected void finalize() {
super.shutdown();
}
}
static class DelegatedExecutorService extends AbstractExecutorService {
private final ExecutorService e;
DelegatedExecutorService(ExecutorService executor) { e = executor; }
public void execute(Runnable command) { e.execute(command); }
public void shutdown() { e.shutdown(); }
public List shutdownNow() { return e.shutdownNow(); }
public boolean isShutdown() { return e.isShutdown(); }
public boolean isTerminated() { return e.isTerminated(); }
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
return e.awaitTermination(timeout, unit);
}
public Future> submit(Runnable task) {
return e.submit(task);
}
public Future submit(Callable task) {
return e.submit(task);
}
public Future submit(Runnable task, T result) {
return e.submit(task, result);
}
public List> invokeAll(Collection extends Callable> tasks)
throws InterruptedException {
return e.invokeAll(tasks);
}
public List> invokeAll(Collection extends Callable> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
return e.invokeAll(tasks, timeout, unit);
}
public T invokeAny(Collection extends Callable> tasks)
throws InterruptedException, ExecutionException {
return e.invokeAny(tasks);
}
public T invokeAny(Collection extends Callable> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return e.invokeAny(tasks, timeout, unit);
}
}
上面给出了和 newSingleThreadExecutor 相关的三个类的源码其都在 Executors 里,通过源码可以知道 newSingleThreadExecutor 创建的是一个只包含一个线程的线程池,因此它可以串行执行,其实这个线程池和 newFixedThreadPool(1) 类似,只是 newSingleThreadExecutor 会在垃圾回收的时候执行 shutdown,然后由于加了一层代理可能功能没有那么全,其他基本一样;
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
从源码可以看出通过 newCachedThreadPool 创建的线程池其实是一个无限的线程池,由于使用的阻塞队列为同步阻塞队列,因此只要有新任务到达,它就会创建一个线程去处理任务,空闲的线程会在 60 秒后被销毁;
由于该线程池的特殊性,因此它不适合处理执行时间较长的任务,如果任务执行时间长,将会产生很多线程,从而让 CPU 不堪重负;
newWorkStealingPool
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
newWorkStealingPool 是 JDK8 提供的新的线程池,可以看到它不是由传统的 ThreadPoolExecutor 来实现的线程池,它是由 JDK7 中提供的 ForkJoinPool 提供的线程池,是对目前线程池的补充;
它创建一个和cpu 核心想等的线程池,用来进行并行任务的执行,它是通过工作窃取的方式,使得创建的线程不会闲置。