java线程池

一 接口与类

先看看线程池相关类之间的关系:

其中,

Executor: 是执行引擎的接口,可以具体化地类比成一台机器,它只有一个方法: void execute(Runnable command). 显然这符合机器的最主要的功能特点, 即: 只需要给它发送指令(任务).

ExecutorService: 是执行引擎管理者(服务)的接口,同理可以具体类比为一个工厂. 这个接口对工厂的主要功能进行了提取和抽象, 提供了机器的启停,状态监控等功能的描述.

ScheduledExecutorService: 这是一种可以可调度的机器管理者(工厂),发送到它上面的指令(任务)可以调度执行,不过它支持的调度模式仅支持周期性执行任务的策略.

ThreadPoolExecutor: 一个工厂为了加快生产过程,内部肯定置办了多台机器,程序中对应的就是线程池.所有的ExecutorService实现类的功能也是直接依赖这个类,这个类的功能特点直接影响java线程池相关的功能,理解这个类的工作模式是理解java线程池的关键.

Excecutors: 普通程序员在使用线程池时,可能更喜欢根据自己这样的模式: 只需要描述自己需要的线程池的特点,就能直接获得线程池.Executors类就是就是出于这样的考虑. 或者说java设计者把常用的线程池的创建工作已经做了,普通程序员只需要选择自己需要的线程池(当然你要清楚自己的功能需求,以及可供选择的几种线程池的特性). 当然如果确实有需要,程序要也可以自己手动创建线程池,java也提供了可供选的基础类,就是前面提到的ThreadPoolExecutor类.

编码开发的时候,最常见的方式都是面向ExecutorService这个接口编程的,所以掌握ExecutorService接口是非常重要的. ExecutorService接口主要的方法如下:

void execute(Runnable command): 这是继承自Executor接口的方法,用于提交任务,如果线程池已经关闭,则会抛出RejectedExecutionException异常.

<T> Future<T> submit(Callable<T> task): 提交实现了的Callable接口的任务.同样,如果线程池已经关闭,则会抛出RejectedExecutionException异常.

void shutdown(): 关闭线程池,但是会等待线程池中所有任务执行完毕,而且不能再向线程中提交新的任务.

List<Runnable> shutdownNow(): 马上关闭线程池.不等所有任务完成,而是强制停止正在运行的任务,并返回等待执行(还没有运行)的任务.

isShutdown(): 线程池是否已经关闭.也即线程池是否已经调用了shutdown()或shutdownNow()等方法.

isTerminated(): 线程池是否已经终止,可以把这时的线程池状态理解为:已经调用了shutdown()或shutdownNow()等方法,而且所有线程都已经运行完毕[对此,还不是很确定,还需要后续检验].

二 各种线程池

前面java设计者已经为们提供了很多现有的线程池,这些线程池已经能满足常规的开发工作,前面也提到我们需要做的就是理解各个线程池的特性,然后根据自己的需求选择适合的线程池.现有的线程池的创建工作封装在Executors类的静态方法中,重要的有下面个(类):

1)newSingleTheadExecutor():  这是最简单的线程池,简单到java设计人员甚至不认为它是一个池,因为它内部只有一个线程,用机器和工厂的类比来说,这更像是一台机器,而不是工厂(包含多台机器).

2)newFixedThreadPool(int count): 固定线程数量的线程池.

3)newCachedThreadPool(): 一种弹性线程池,它基于这样的策略来管理池中的线程: 线程不够的就新建,一段时间内没有使用就回收.它试图在这两个问题之间做好平衡: 创建线程耗资源, 维护线程同样也耗资源.

4)newScheduledThreadPool(int count): 它是为这样的需求提供的: 周期性的执行某个任务.

前面说明了各种线程池的特性,但是该怎么选择呢?

single线程池和schedule线程的使用场景比较明显也比较少用到.经常用到的是fixed线程池和cached线程池.

single线程池

single线程适池用于并发量不大,不需要并发甚至不能并发的场景,而且还有一个重点: single线程可以确保提交给它的任务顺序执行,这在很多需求下很有用.

schedule线程池

这种线程池的使用场景比较明显

fixed线程池和cached线程池

这两个线程池其实都是在线程创建和线程维护之间取舍.根本上看,可以从这三个方面考虑:

1)任务量

2)提交任务的频度

3)单个任务执行的耗时长短

一般来说:

任务量大, 提交任务频度小: 使用cached线程池

任务量小, 单个任务执行的耗时长: 使用fixed线程池

其他场景的化就得具体分析了.

三 ThreadPoolExecutor分析

其实前面提到的各种线程池都是基于ThreadPoolExecutor类.它们的很多特性都依赖ExecutorThreadPool.其中最主要的两个特性就是线程数(fixed线程池)和线程回收(cached线程池).

从ThreadPoolExecutor的构造函数就能很容易理解这一点,ThreadPoolExecutor的构造函数如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue)

其中参数的含义:

corePoolSize: 初始化的线程池中的线程数

maximumPoolSize: 线程池中最大线程数

keepAliveTime: 线程的生命长短,用于空闲线程回收,0表示线程池中的空闲线程永远维护,不回收

unit: 时间单位

workQueue: 任务队列

显然,maximumPoolSize和keepAliveTime两个参数就能支撑fixed线程池中的线程数和cached线程池的线程回收特性.

四 调度线程池

ScheduledExecutorService用的不多,留待以后补充.

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>