线程池与Executor 框架

本节思维导图: 

线程池与Executor 框架

思维导图源文件+思维导图软件关注微信公众号:“Java面试通关手册” 回复关键字:“Java多线程” 免费领取。

一 使用线程池的好处

线程池提供了一种限制和管理资源(包括执行一个任务)。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。 这里借用《Java并发编程的艺术》提到的来说一下使用线程池的好处

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

  • 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。

  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

二 Executor 框架

2.1 简介

Executor 框架是Java5之后引进的,在Java 5之后,通过 Executor 来启动线程比使用 Thread 的 start 方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免 this 逃逸问题。

补充:this逃逸是指在构造函数返回之前其他线程就持有该对象的引用. 调用尚未构造完全的对象的方法可能引发令人疑惑的错误。

2.2 Executor 框架结构(主要由三大部分组成)

1 任务。

执行任务需要实现的Runnable接口Callable接口。 Runnable接口Callable接口实现类都可以被ThreadPoolExecutorScheduledThreadPoolExecutor执行。

两者的区别:

Runnable接口不会返回结果但是Callable接口可以返回结果。后面介绍Executors类的一些方法的时候会介绍到两者的相互转换。

2 任务的执行

如下图所示,包括任务执行机制的核心接口Executor ,以及继承自Executor 接口的ExecutorService接口ScheduledThreadPoolExecutorThreadPoolExecutor这两个关键类实现了ExecutorService接口

注意: 通过查看ScheduledThreadPoolExecutor源代码我们发现ScheduledThreadPoolExecutor实际上是继承了ThreadPoolExecutor并实现了ScheduledExecutorService ,而ScheduledExecutorService又实现了ExecutorService,正如我们下面给出的类关系图显示的一样。

ThreadPoolExecutor类描述:

ScheduledThreadPoolExecutor类描述:

线程池与Executor 框架

3 异步计算的结果

Future接口以及Future接口的实现类FutureTask类。 当我们把Runnable接口Callable接口的实现类提交(调用submit方法)给ThreadPoolExecutorScheduledThreadPoolExecutor时,会返回一个FutureTask对象

我们以AbstractExecutorService接口中的一个submit方法为例子来看看源代码:

上面方法调用的newTaskFor方法返回了一个FutureTask对象。

2.3 Executor 框架的使用示意图

线程池与Executor 框架

 1. 主线程首先要创建实现Runnable或者Callable接口的任务对象。 备注: 工具类Executors可以实现Runnable对象和Callable对象之间的相互转换。(Executors.callable(Runnable task)或Executors.callable(Runnable task,Object resule))。

 2. 然后可以把创建完成的Runnable对象直接交给ExecutorService执行(ExecutorService.execute(Runnable command));或者也可以把Runnable对象或Callable对象提交给ExecutorService执行(ExecutorService.submit(Runnable task)或ExecutorService.submit(Callable task))。

执行execute()方法和submit()方法的区别是什么呢/strong> 1)execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否; 2)submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。

  3. 如果执行ExecutorService.submit(…),ExecutorService将返回一个实现Future接口的对象(我们刚刚也提到过了执行execute()方法和submit()方法的区别,到目前为止的JDK中,返回的是FutureTask对象)。由于FutureTask实现了Runnable,程序员也可以创建FutureTask,然后直接交给ExecutorService执行。

 4. 最后,主线程可以执行FutureTask.get()方法来等待任务执行完成。主线程也可以执行FutureTask.cancel(boolean mayInterruptIfRunning)来取消此任务的执行。

三 ThreadPoolExecutor详解

线程池实现类ThreadPoolExecutor是Executor 框架最核心的类,先来看一下这个类中比较重要的四个属性

3.1 ThreadPoolExecutor类的四个比较重要的属性

线程池与Executor 框架

3.2 ThreadPoolExecutor类中提供的四个构造方法

我们看最长的那个,其余三个都是在这个构造方法的基础上产生(给定某些默认参数的构造方法)

3.3 如何创建ThreadPoolExecutor

方式一:通过构造方法实现(官方API文档并不推荐,所以建议使用第二种方式) 

线程池与Executor 框架

方式二:通过Executor 框架的工具类Executors来实现 我们可以创建三种类型的ThreadPoolExecutor:

  • FixedThreadPool

  • SingleThreadExecutor

  • CachedThreadPool

对应Executors工具类中的方法如图所示: 

线程池与Executor 框架

3.4 FixedThreadPool详解

FixedThreadPool被称为可重用固定线程数的线程池。通过Executors类中的相关源代码来看一下相关实现:

来源:pengzhisen123

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2018年9月4日
下一篇 2018年9月4日

相关推荐