深入浅出 Java Concurrency (29): 线程池 part 2 Executor 以及Executors[转]

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

下面这张图完整描述了线程池的类体系结构。

首先Executor的execute方法只是执行一个Runnable的任务,当然了从某种角度上将最后的实现类也是在线程中启动此任务的。根据线程池的执行策略最后这个任务可能在新的线程中执行,或者线程池中的某个线程,甚至是调用者线程中执行(相当于直接运行Runnable的run方法)。这点在后面会详细说明。

ExecutorService在Executor的基础上增加了一些方法,其中有两个核心的方法:

  • Future<?> submit(Runnable task)
  • <T> Future<T> submit(Callable<T> task)

这两个方法都是向线程池中提交任务,它们的区别在于Runnable在执行完毕后没有结果,Callable执行完毕后有一个结果。这在多个线程中传递状态和结果是非常有用的。另外他们的相同点在于都返回一个Future对象。Future对象可以阻塞线程直到运行完毕(获取结果,如果有的话),也可以取消任务执行,当然也能够检测任务是否被取消或者是否执行完毕。

在没有Future之前我们检测一个线程是否执行完毕通常使用Thread.join()或者用一个死循环加状态位来描述线程执行完毕。现在有了更好的方法能够阻塞线程,检测任务执行完毕甚至取消执行中或者未开始执行的任务。

ScheduledExecutorService描述的功能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。这包括延迟时间一次性执行、延迟时间周期性执行以及固定延迟时间周期性执行等。当然了继承ExecutorService的ScheduledExecutorService拥有ExecutorService的全部特性。

ThreadPoolExecutor是ExecutorService的默认实现,其中的配置、策略也是比较复杂的,在后面的章节中会有详细的分析。

ScheduledThreadPoolExecutor是继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现,在后面的章节中会有详细的分析。

这里需要稍微提一下的是CompletionService接口,它是用于描述顺序获取执行结果的一个线程池包装器。它依赖一个具体的线程池调度,但是能够根据任务的执行先后顺序得到执行结果,这在某些情况下可能提高并发效率。

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

  • newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
  • newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
  • newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
  • newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
  • newSingleThreadScheduledExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

在详细讲解ThreadPoolExecutor的时候会具体讨论上述参数配置后的意义和原理。

线程池是一个复杂的任务调度工具,因此它涉及到任务、线程池等的生命周期问题,在下一节中来探讨下这个问题。

时间: 12-01

深入浅出 Java Concurrency (29): 线程池 part 2 Executor 以及Executors[转]的相关文章

深入浅出 Java Concurrency (30): 线程池 part 3 Executor 生命周期[转]

我们知道线程是有多种执行状态的,同样管理线程的线程池也有多种状态.JVM会在所有线程(非后台daemon线程)全部终止后才退出,为了节省资源和有效释放资源关闭一个线程池就显得很重要.有时候无法正确的关闭线程池,将会阻止JVM的结束. 线程池Executor是异步的执行任务,因此任何时刻不能够直接获取提交的任务的状态.这些任务有可能已经完成,也有可能正在执行或者还在排队等待执行.因此关闭线程池可能出现一下几种情况: 平缓关闭:已经启动的任务全部执行完毕,同时不再接受新的任务 立即关闭:取消所有正在

深入浅出 Java Concurrency (35): 线程池 part 8 线程池的实现及原理 (3)[转]

线程池任务执行结果 这一节来探讨下线程池中任务执行的结果以及如何阻塞线程.取消任务等等. 1 package info.imxylz.study.concurrency.future;2 3 public class SleepForResultDemo implements Runnable {4 5     static boolean result = false;6 7     static void sleepWhile(long ms) {8         try {9      

深入浅出 Java Concurrency (33): 线程池 part 6 线程池的实现及原理 (1)[转]

线程池数据结构与线程构造方法 由于已经看到了ThreadPoolExecutor的源码,因此很容易就看到了ThreadPoolExecutor线程池的数据结构.图1描述了这种数据结构. 图1 ThreadPoolExecutor 数据结构 其实,即使没有上述图形描述ThreadPoolExecutor的数据结构,我们根据线程池的要求也很能够猜测出其数据结构出来. 线程池需要支持多个线程并发执行,因此有一个线程集合Collection<Thread>来执行线程任务: 涉及任务的异步执行,因此需要

深入浅出 Java Concurrency (28): 线程池 part 1 简介[转]

从这一节开始正式进入线程池的部分.其实整个体系已经拖了很长的时间,因此后面的章节会加快速度,甚至只是一个半成品或者简单化,以后有时间的慢慢补充.完善. 其实线程池是并发包里面很重要的一部分,在实际情况中也是使用很多的一个重要组件. 下图描述的是线程池API的一部分.广义上的完整线程池可能还包括Thread/Runnable.Timer/TimerTask等部分.这里只介绍主要的和高级的API以及架构和原理. 大多数并发应用程序是围绕执行任务(Task)进行管理的.所谓任务就是抽象.离散的工作单元

深入浅出 Java Concurrency (34): 线程池 part 7 线程池的实现及原理 (2)[转]

线程池任务执行流程 我们从一个API开始接触Executor是如何处理任务队列的. java.util.concurrent.Executor.execute(Runnable) Executes the given task sometime in the future. The task may execute in a new thread or in an existing pooled thread. If the task cannot be submitted for execut

深入浅出 Java Concurrency (36): 线程池 part 9 并发操作异常体系[转]

并发包引入的工具类很多方法都会抛出一定的异常,这些异常描述了任务在线程池中执行时发生的例外情况,而通常这些例外需要应用程序进行捕捉和处理. 例如在Future接口中有如下一个API: java.util.concurrent.Future.get(long, TimeUnit) throws InterruptedException, ExecutionException, TimeoutException; 在前面的章节中描述了Future类的具体实现原理.这里不再讨论,但是比较好奇的抛出的三

深入浅出 Java Concurrency - 目录 [转]

这是一份完整的Java 并发整理笔记,记录了我最近几年学习Java并发的一些心得和体会. J.U.C 整体认识 原子操作 part 1 从AtomicInteger开始 原子操作 part 2 数组.引用的原子操作 原子操作 part 3 指令重排序与happens-before法则 原子操作 part 4 CAS操作 锁机制 part 1 Lock与ReentrantLock 锁机制 part 2 AQS 锁机制 part 3 加锁的原理 (Lock.lock) 锁机制 part 4 锁释放与

Java多线程(四) 线程池

一个优秀的软件不会随意的创建很销毁线程,因为创建和销毁线程需要耗费大量的CPU时间以及需要和内存做出大量的交互.因此JDK5提出了使用线程池,让程序员把更多的精力放在业务逻辑上面,弱化对线程的开闭管理. JDK提供了四种不同的线程池给程序员使用 首先使用线程池,需要用到ExecutorService接口,该接口有个抽象类AbstractExecutorService对其进行了实现,ThreadPoolExecutor进一步对抽象类进行了实现.最后JDK封装了一个Executor类对ThreadP

(CZ深入浅出Java基础)线程笔记

一.线程的引入 1.多线程概述 1.1.进程 a.正在运行的程序,是系统进行资源分类和调用的独立单位. b.每个进程都有它自己的内存空间和系统资源. 1.2.线程 a.是进程中的单个顺序控制流,是一条执行路径. b.一个进程如果只有一条路径,则称为单线程程序. c.一个进程如果有多条执行路径,则称为多线程程序. 1.3.小结 线程多的进程抢到CPU执行权的概率大,但是仍具有随机性. 2.Java程序运行原理 2.1.Java运行 Java命令会启动Java虚拟机,启动JVM,等于启动了一个应用程