Java并发编程框架Executor总结

猪小花1号2018-09-05 09:46

作者:廖祥俐


大多数并发应用程序都是围绕着“任务执行 ”进行构造的:任务通常是一些抽象的且离散的工作单元,通过把应用程序的工作分解到多个任务中。在理想状态下,各个任务之间是相互独立的:任务并不依赖于其他任务的状态,结果或边界效应,独立性有助于实现并发。

采用多线程往往基于以下几个情境:

  • 让任务处理过程从主线程中分离出来
  • 任务可以并行处理,提高处理速度
  • 任务代码可以保证是线程安全的

然而,在使用多线程的时候,显示的为每一个任务分配一个线程有一定的缺陷,尤其当需要创建大量线程的时候,主要有以下几方面:

  1. 线程生命周期的开销非常高;
  2. 资源消耗,如果可运行的线程数量远远多于可用处理器数量,则有些线程被闲置,会占用大量资源;
  3. 影响系统稳定性,可创建线程的数量上均存在一定的限制,这个限制受多种因素影响,如JVM启动参数,Thread构造函数请求栈大小等,如果超出限制很可能出现OOM异常。

针对此,java.util.concurrent包提供了一种灵活线程管理框架:Executor框架,任务执行的主要抽象不是Thread,而是Executor。

public interface Executor{
    void execute(Runnable command);
}


1, Executor简介

Java从1.5版本开始,为简化多线程并发编程,引入全新的并发编程包:java.util.concurrent及其并发编程框架(Executor框架)。 Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。


2, 子类


ExecutorService:在ScheduledExecutorService基础上,添加了一些用于管理生命周期的方法接口,同时还有一些用于任务提交的方法接口

AbstractExecutorService:提供 ExecutorService 执行方法的默认实现,此类使用包中提供的默认 FutureTask 类实现了 submit、invokeAny 和 invokeAll 方法

ScheduledExecutorService:在ExecutorService的基础上,ScheduledExecutorService提供了按时间安排执行任务的功能,它提供的方法主要有:

public interface ScheduledExecutorService extends ExecutorService {
    public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,  long delay, TimeUnit unit);
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay,long period,TimeUnit unit);
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay,TimeUnit unit);
}

ThreadPoolExecutor(继承AbstractExecutorService):ThreadPoolExcutor为一些Executor提供了基本的实现,这些Executor是由Executors中的工厂 newCahceThreadPool、newFixedThreadPool和newScheduledThreadExecutor返回的

ScheduledThreadPoolExecutor:ScheduledThreadPoolExecutor类实现了ScheduledExecutorService接口中定义的以不同方法执行任务的方法


3, 生命周期

JVM只有在所有(非守护)线程全部终止后才会退出,在使用Executor的时候,需要考虑如何正确关闭Executor,不然会导致JVM无法结束。

由于Executor是异步地执行任务,因此在任何时间,之前提交的任务状态都不是立即可见的,有些任务已经完成,有些正在运行,而还有些在等待队列中等待执行。

在关闭应用时,可能采取最平缓的关闭方式(完成所有已经启动的任务,并且不再接受新的任务),也有可能是其它方式,在关闭的时候,希望Executor能够记录受到影响的任务,并可以返回相应的状态。

为了解决执行服务的生命周期问题,Executor扩展了ExecutorService接口,添加了一些用于管理生命周期的方法(同时还有一些用于任务提交的方法)

public interface ExecutorService extends Executor {  
    void shutdown();  
    List<Runnable> shutdownNow();  
    boolean isShutdown();  
    boolean isTerminated();  
    boolean awaitTermination(long timeout, TimeUnit unit)  
    ……
}

一个Executor的生命周期有三种状态运行、关闭和终止。

Executor创建时处于运行状态。当调用ExecutorService.shutdown()后,处于关闭状态,isShutdown()方法返回true。这时,不应该再向Executor中添加任务,所有已添加的任务执行完毕后,Executor处于终止状态,isTerminated()返回true。

如果Executor处于关闭状态,往Executor提交任务会抛出unchecked exception RejectedExecutionException, 这里主要是当Executor处于关闭状态时,任务会被转交到RejectedExecutionHandler去执行,RejectedExecutionHandler使得execute方法抛出RejectedExecutionException异常


4, 线程池种类及使用场景

Executors中通过静态工厂方法提供了4种基本的线程池创建方法:

1,newCachedThreadPool

缓存型池子,先查看池中有没有以前建立的线程,如果有,就reuse.如果没有,就建一个新的线程加入池中

-缓存型池子通常用于执行一些生存期很短的异步型任务,因此在一些面向连接的daemon型SERVER中用得不多。

-能reuse的线程,必须是timeout IDLE(Timeout waiting for idle object)内的池中线程,缺省timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。

注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。

2,newFixedThreadPool

-newFixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程

-其独特之处:任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子

-和cacheThreadPool不同,FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器

-从方法的源代码看,cache池和fixed 池调用的是同一个底层池,只不过参数不同: fixed池线程数固定,并且是0秒IDLE(无IDLE) cache池线程数支持0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60秒IDLE

3,ScheduledThreadPool

-调度型线程池

-这个池子里的线程可以按schedule依次delay执行,或周期执行

4,SingleThreadExecutor

-单例线程,任意时间池中只能有一个线程

-用的是和cache池和fixed池相同的底层池,但线程数目是1-1,0秒IDLE(无IDLE)


5, 使用注意点

1,类的可变状态时至关重要的:所有的并发问题都可以归纳为如何协调对可变状态的访问,可变状态越少,越容易保证线程的安全性;

2,尽量将将类的域声明为final类型,除非它们需要是可变的,这样有利于线程安全(不可变对象一定是线程安全的)

3,使用Executor优先于线程:涉及到线程或线程池相关,均需要考虑如何优雅的中止线程(需要精细的代码),尽可能通过Excutor框架来管理线程的执行。

4,newCachedThreadPool适合有大量短生命周期的线程场景,在生产环境中一般采用newFixedThreadPool固定线程池大小。


参考

Java并发编程 - Executor,Executors,ExecutorService, CompletionServie,Future,Callable

《Effective Java》

《Java并发编程实战》



网易云大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者廖祥俐授权发布