《Java线程池》Executor 以及Executors
线程池的相关类的体系结构如下:
先看Executor接口,是最根上的接口
public interface Executor {
/*
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*翻译:
1、任务可能在将来的某个时刻被执行,即可能不是立即执行
2、任务可能在新的线程中被执行,
新线程可能是线程池中的,也可能是被调用的线程。这取决于对Executor的实现
*/
void execute(Runnable command);
}
首先Executor的execute方法只是执行一个Runnable的任务,当然了从某种角度上将最后的实现类也是在线程中启动此任务的。根据线程池的执行策略最后这个任务可能在新的线程中执行,或者线程池中的某个线程,甚至是调用者线程中执行(相当于直接运行Runnable的run方法)。这点在后面会详细说明。
ExecutorService在Executor的基础上增加了一些方法,其中有两个核心的方法:
1、Future<?> submit(Runnable task)
2、<T> Future<T> submit(Callable<T> task)
这两个方法都是向线程池中提交任务,它们的区别在于Runnable在执行完毕后没有结果,Callable执行完毕后有一个结果。这在多个线程中传递状态和结果是非常有用的。另外他们的相同点在于都返回一个Future对象。Future对象可以阻塞线程直到运行完毕(获取结果,如果有的话),也可以取消任务执行,当然也能够检测任务是否被取消或者是否执行完毕。在没有Future之前我们检测一个线程是否执行完毕通常使用Thread.join()或者用一个死循环加状态位来描述线程执行完毕。现在有了更好的方法能够阻塞线程,检测任务执行完毕甚至取消执行中或者未开始执行的任务。
ScheduledExecutorService描述的功能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。这包括延迟时间一次性执行、延迟时间周期性执行以及固定延迟时间周期性执行等。当然了继承ExecutorService的ScheduledExecutorService拥有ExecutorService的全部特性。
ThreadPoolExecutor是ExecutorService的默认实现,ThreadPoolExecutor并不是直接实现的ExecutorService接口,而是继承AbstractExecutorService抽象类。AbstractExecutorService抽象类实现了ExecutorService接口。ThreadPoolExecutor其中的配置、策略也是比较复杂的,在后面的章节中会有详细的分析。
public class ThreadPoolExecutor extends AbstractExecutorService ;
ScheduledThreadPoolExecutor是继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现,继承体系如下,在后面的章节中会有详细的分析。
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService
这里需要稍微提一下的是CompletionService接口,它是用于描述顺序获取执行结果的一个线程池包装器。它依赖一个具体的线程池调度,但是能够根据任务的执行先后顺序得到执行结果,这在某些情况下可能提高并发效率。
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。
1、newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
2、newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
3、newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
4、newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
5、newSingleThreadScheduledExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
小结
关于Executor,我们需要了解它的体系结构,这样我们就能更容易的理清楚他们的关系。
关于Executors,我们需要记住他所能产生的几种线程池的方法,以及产生的每个线程池的类别是什么,这个要相当的清楚,这样我们就可以在合适的场景使用合适的线程池。
现在回顾一下:
1、newSignalThreadExecutor():产生单线程的线程池,即只有一个线程来顺序执行任务队列中的任务。当此线程因异常结束,则产生一个新的线程来补充。
2、newFixdThreadPool():产生固定个数的线程池。
3、newCachedThreadPool(int num):产生一个缓存线程池,即当线程数大于任务数之后回收多余的线程。
4、newScheduledThreadPool():产生一个支持周期性执行任务的线程池。例如:
//创建一个线程数为5的线程池,支持周期执行任务
ScheduledExecutorService exec = Executors.newScheduledThreadPool(5);
5、newSignalScheduledThreadPool():产生一个单个线程的线程池,且线程池支持周期性执行任务。
参考资料
1、http://www.blogjava.net/xylz/archive/2010/12/21/341281.html