一、使用Java线程池的好处
(1)降低资源消耗。通过重复利用已创建的线程降低反复创建和销毁线程的消耗
(2)提高响应速度。任务不需要等线程创建就可以执行
(3)提高线程的可管理性。使用线程池可以统一分配,调优和监控
二、线程池对任务的处理流程
(1)线程池判断核心线程池中的线程是否有空余的。如果有,就创建新的工作线程完成任务,没有就下一步
(2)线程池判断工作队列是否满了。如果没满就存在工作队列中,满了就下一步
(3)线程池判断是否已经满了,没满就创建普通线程来执行,满了就交给饱和策略去处理
可以参照下面两幅图进行理解:
具体的工作队列和饱和策略之后再进行讲解
三、Executor框架
框架图如下:
由此图可见,我们最基本的两大线程池类都是从Executor->ExecutorService->再继续往下实现,由此我们一步一步来:
(1)Executor接口
它是Executor框架的基础,是任务运行的简单接口,其中之定义了一个executor执行方法,不同的实现类,executor方法也就不一样
public interface Executor {
void execute(Runnable command);
}
(2)ExecutorService接口
它扩展了Executor的接口,提供了一些管理执行器和任务的声明周期的方法
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
}
其中submit方法的参数可以是Callable的实例,跟其父接口的executor方法相比,可提供返回值(因为Callable有返回值)
(3)ScheduledExecutorService接口
其实现了ExecutorService接口,支持Future和定期执行任务
public interface ScheduledExecutorService extends ExecutorService {
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
(4) AbstractExecutorService实现类
对其父接口的方法进行了实现和重写,实现了一些逻辑
(5)ThreadPoolExecutor实现类(重点)
ThreadPoolExecutor通常使用工厂类Executors来实现(后文进行讲解)。
它的构造方法需要几个参数:
corePoolSize:核心线程数
maximumPoolSize:最大线程数(包括核心线程和一般线程)
BlockingQueue:暂时保存任务的工作队列
RejectedExecutionHandler:当线程池关闭或者饱和时,executor要调用的handler处理方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
Executors可以创建3种类型的ThreadPoolExecutor:SingleThreadExecutor,FixedThreadPool,CachedThreadPool。
下面分别了来介绍这3种线程池:
1)SingleThreadExecutor,创建单个线程的线程池,适用于保证循序的执行各个任务,并且在任意时间不会有多个线程是活动的。(只有一个核心线程)
2)FixedThreadPool,创建固定线程数的线程池,适用于限制线程数量的应用(只有核心线程)
3)CachedThreadPool,是创建大小无界的线程池,适用于执行很多短期任务的小程序。当需要线程并无缓存线程可用时,就创建新的线程;如果线程闲置的时间超过阈值则会被终止并移除缓存。(没有核心线程,都是临时线程,工作队列没有容量限制)
(6)ScheduledThreadPoolExecutor实现类(重点)
它实现了ScheduledExecutorService接口,并继承了ThreadPoolExecutor类,主要用来在给定的延迟之后运行任务,或者定期执行任务
有2种线程池:
1)SingleThreadScheduledExecutor:单个线程进行定时或者周期性的调度工作
2)ScheduledThreadPool:多个线程进行定时或者周期性的调度工作
(7)ForkJoinPool
这是JDK8才引入的线程池的创建方法,继承了AbstractExecutorService,实现了Fork/Join框架
可以创建1种线程池
1)WorkStealingPool:内部会构建ForkJoinPool,利用working-stealing算法,并行的处理任务,不保证处理顺序
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
public ForkJoinPool(int parallelism,
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
boolean asyncMode) {
this(checkParallelism(parallelism),
checkFactory(factory),
handler,
asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
"ForkJoinPool-" + nextPoolId() + "-worker-");
checkPermission();
}
四、什么是Fork/Join框架?
是一个把大任务分成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架,其中实现了work-stealing算法(下文讲)
(1)work-stealing算法
指某个线程从其他队列里窃取任务来执行,但是会尝试竞争,于是通常采用双端队列,被窃取任务线程永远从头部拿,窃取的从尾部拿。
任务流程图:
优点:充分利用线程进行并行运算,减少线程间竞争