2023-01-14
原文作者: HelloWorld_EE 原文地址:https://blog.csdn.net/u010412719/category_6159934_2.html

《Java线程池》Executor 以及Executors

线程池的相关类的体系结构如下:

ThreadPool.PNG

先看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

阅读全文