前语

肥壕最近在温习线程这一块常识ExecutorExecutorServiceThreadPoolExecutor 这三兄弟总感觉很难辨认,每次看完后没过多久又会忘,所以今日特地来盘一下 Executor 结x B U + k构。

正文

Executors 是在 JDI Y 4 % D v dK1.5 引入的,位于 java.util.concu{ U } Z }rrent包下,其首要意图是简化线程调用,办理线程的生命周期(启动、履行、封闭)。N A 3 n / ~

在 JDK1.5 之前咱们运用线程的姿态是:

new Thread(new RunnableTask()).start()

JDK1.5之V # N W l Z U 1后呢M ? u e l v,咱们能够运用 Executor 直接履行的 Runnable 完结类:

// 1.创建详细的ExE e &ecuK C i 2  ?tor目标
Exec_ N zutor ex = new MyExecutor
// 2.y B f U l [ 5 a 6调用execute办法履行使命  
ex.execute(new RunnableTaks())

这两种办法比照,很显然第二种更为优雅。咱们的关注点是把使6 T V s h b交给履行器,至于使命的怎样履行咱们是不需关怀的,这也完结了使命调用者之间的解耦。

咱们先看一下 Executors 中各个类之间的依赖图:

  • Executor:界说办法 execute(Runnable command),该办法接纳一个 Runable 实例

  • ExecutorService: 承继 Exe3 m 2 = scutor 接口,并供给了生命周期办理的办法,以及能够盯梢异步使命] F s ?行情况回来 Future 的办法

  • Abstra! ^ 1ctExecutorService:抽象类,完结 ExecutorService 接口

  • ThreadPoolExecutor:是 Executoi 9 w ~ +rService* O L F X V O g 的一个完结类,承继 AbstractExecutorService 。这是Java线程池最中心的一个类。首要功能是创建线程池,给使命分配线程资源,履行使命W C 9

  • Schm d eeduledExecutk t [orService:承继 ExecutorService 接口,界说了推迟履行和周期履行的办法

  • ScheduledThreadPoolExecutorV 2 k ] C W H I NScheduC N n t H 1 yledExecutorService 的完: d { ] @ h A p .结类,完c 4 % = t j结了推迟履行和周期履行的办法

  • Executors:静态工厂类,该类界说了一系列静态工厂办法,经过这些工厂办法能够创建不同类型的线程池

下面咱们详细看一下D d t每个类的e + H b特点和办法

Executor

void execute(Runnable command);

该接口首要的意图就是解耦使命处理机制中的使命提交使命如何运转`(也包括线程的运用,调度)

ExecutoR P 9 . ; 6rService

封闭线程池

void shutdown()i H / U ( U k # /;
List&B 9 y 3 4 T L : klt;R; = @ ; n X g Xunnable> shutdownNow();

提交线程使命

<T> Future<T> submit(Callu h O l Rable<T> task);
<T> Future<T>y ( ~ submit(Runnable task, T res$ & . ^ G ? d ] ~ult);
Future<?> submit(Runnable task);

同步履行

boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
<T> List<- ( E 8 XFutug E C L & !re<T>> invokeAll(Collection<? extends Cp Z Nallable<T>> tasks) throws InterruptedException;
<T> LiT . & ( _ ;st<Future<T>> invokeAll(Collection<? extends Callable<T>&A ? 0 `gt; tasks,long timU r # K = seout, TimeUnit unit) throws InterruptedException;
<T> T invokeAny(t R 8 : /Collection<? extends Callr / 3 K * X b | %able<T>> tasks) thr| B % ] Hows Inter2 & M K % s ZruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedExceptionF J m 3 c 8 8, ExecO k 3 , ` |utionException, TimeoutException;

AbstractExecutorService

咱们重点关注一下这几个办法:

public Future<?> submit] 7 0(Runnable task) {
iM _ + J d f # f (task == null) throw new NullPointerException();
RunnableF+ ` * T V 8 U b *uture<Void> ftask = newTaskFor(task, nu_ c o = T Gll);
execute(ftask)O E N $ S;
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask =+ @ k p p ? 8 newTaskFor(task, result)Y v b T;
execute(ftask);
return ftask;
}
public <T> Fu5 o  J , ] n Sture<T> submit(Callable<T>W f R ) L # n O B task) {
if (task == null) throw new NullPointerException();
Runnah K r A Y w -  DbleFuture<T> ftask = newTaskFor(task);
execute(ftask: W = q h k D);
return f$ 8 b X v u A dtasd h J 6 B { w 9 Xk;
}

能够看到 submit 办法能接纳 RunD b H 3 G X ^ HnableCallable 实例的参数。

submit 办法里边将 Runnable 和 Callable 再封装 RunnableFuture 目标,而 RunnableFuture 对完结类是 FutureTa7 r ( 1 Q F usk

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value)5 ] D R # {
rm P 3 ^ , T a ] seturn n8 / o D - vew FutureTask{ y B<T>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new Futurev m ; f u Q y yTask<o m l 6 r v D |T>= j 9 a C;(callable);
}

有关 Future 下面会再详细阐明,这儿n w [ G Sf b p就暂时只需要了解一下就好啦。

ThreadPoolExecutor

这是线程池中心类,要解说的东西就比较多啦,感觉要开一篇有关线程池的文章重点解说吧。咱们这儿只需要能敷衍面试就足够了。

看一下几个重要的= & E b v特点:

// 中心线程数
private volatile int corePoolSize;
// 最大线程数
private volatie 4 3 A - { tle int maximumPoolSize;
// 超越中心线程数时闲置线程的存活时刻
private volatile long keepAliveTime;
// 使命履行前保存使命的行O @ h n + G O X r
private final BlockingQueue<d @ 4 P N;Runnable> workQueue;
// 拒绝策略
private static fina3 f [ M $ v E o #l RejectedExecutionHandler defaultHandler = new AbortPolicy();

中心办法:

public void exece s # V I - + o qute(Runnable command)r ~ F {
if (command == null)
throw new Null0 i A gPointerException()6 q M Z Z m R ;
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task.  The caL ~ f H kll to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shour P E } @ldn't, by returg / - KninS c | Q m 0g false.
*
* 2. If a task can be successfully queued, then we sti: K o }ll need
* to double-check whethi v : u # qer we should have added{ f ` k 8 4 a thread
* (because existing ones die 1 ( } s Bd since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state anI + a Z  G 0 * Id if necessary roll backb D U the enqueuingA f x R if
* sp v 1topped, or start a new threb N 0 Z 0 ~ J T 5ad if thD V c & A P H X 1ere are none.
*
* 3. If we cannot queue task,D i a 8 F  z then we try to&  } add a new
* thread.  If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.ge* C 1 1 St();
if (workerCountOf(c)3 t z [ ) 1 r m < cor[ T 4ePoolSize0 : K - n) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(reche1 ` - 2ck) == 0)
addWorker(null, false);
}
else if (!addWorkern { J i t |(command, false))
reject(command);
}

这个办法r – . +是无回来S t ? v 4值的,那对于想盯梢使命的要怎样运用呢$ } l u z

其实这儿运用了模板办法, 上x g u r f V c面说过 AbstractExecutorService& x = } : U K 中供给了三个回来 Future 目标的 sub] $ b m Nmit 办法,办法里边使命最终的履行是调用q / v _了execute()。所以要盯梢使命的话,直接调用 submit 办法即可。

这儿简略差异一下 shutdd D O Uown()shutdownNow() 这两个办法

  • sh3 K b b hutdk U g bown()

    调用 shutdown() 办法后,线程池中止接纳新的使命,可是现已 subme q f h hit 的使命会等候履行完结。假如咱们再向线程池中提交使命,将会抛 RejectedExecutionException 反常。假如线程池的 shutdown() 办法现已调用过,重复调用没有额外p & { }效应。留意,当咱们调用 shutdown() 办法后,会立即从该办法中回来而不会堵塞等候线程池封闭再回来,假如R G v n H期望堵塞等候能够调用 awaitTermination() 办法

  • shutdownNow()

    shuI O 5 x [ ptdownNow() 办法和 shutdown() 办法根本b T p m 9 F [一样,不同的是

    1. 等候行列中的使命不l 6 V T B # | I会履行,并直接回来这些等候履行& P ] W 3 d. 5 { h 5 a ,使命
    2. 测验中止现在履行的使命,调用worker线程的interrupt办法去停止运转的使命。

Executors

工厂类,供给了不同类型的静态创建线程池的办法。

下面咱们就看看 Executors 中的办法:

  • SingleThreadExecutor线程池

    只要一个中心线程在作业,也就是相当于单线程串行履w A 8行所有使命。假如这个仅有的线程由于反常完毕,
    那么会有一个新的线程来代替它。此线程池保证G 5 G所有使命的履行顺序依照使命的提交顺序履行。

    • corePoolSize:1,只要一个中心线程在作业
    • maximumPoolSize:1
    • ks ! Q Q J b 9eepAlix { A veTime:0L
    • workQueue:new LinkedBlockingQueue(),其缓冲行列是无界的
  • FixedThreadPool线程池

    固定巨细的线程池,只要中心线程。每次提交一个使命就创建一个线程,直到线程到达线程池的最大巨细。

    线程池的巨细一旦到达最大值就会坚持不变,假如某个线程由于履行反常而完毕,那么线程池会补充一个新线程。

    FixedThrea7 [ ^ / qdPool 多数针对一些很稳定很固定的正规并发线程

    • corePoolSize:nThreads

    • m, h U v g – #aximumPoolSize:nThreads

    • ke# T n g g % DepAliveTim5 S i , y ] ) U _e:0L

    • workQueue:new LinkedBlocky F 6 k , / aingQueue(),其缓冲行列是无界的

  • CachedThreadPool线程池

    无界线程池,假如线程池的巨细超越了处理使命所需要的线程,那么就会回收部分空闲(60 秒不履行使命)线程,

    当使命数增加时,此线程池又能够智能的添加新线程来处理使命。

    线程池巨细彻底依赖于操作系统(或者说 JVM)能够创建的最大线程巨细。SynchronousQueue 是一个是缓冲区为 1 的I F , s c l C l堵塞行列。

    缓存型池子通常用于履行一些生存期很短的异步型使命,因此在一些面向连接的 da0 o ) ! S n U lemon 型 SERVEZ Z K H A p ; %R 顶用得不多。

    但对于生存期短的异步使命,它是 Executor 的首选

    • corePoolSize:0

    • maxiQ q C * = +mumPoolSizc + 4 ~ 8 oe:Integer.MAX_VALUE

    • keepAliveTimm @ y [ P ^ [ Se:60L

    • workQueue:new SynchronousQueue(),一个是缓冲区为 1 的堵塞行列

  • Sl U 7 @ z Z s (cheduledThreadPool线程池

    中心线= N E h程池固定巨细无限的线程池。此线程池支撑定时以及周期性履行使命的需求。

    创建一个周期性履行使命的线程池。假如闲置,非中心线程池会在 DEFAULT_KEEPALIVEMILLIS 时刻内回收。

    • corePoolSize:corePoolSizy | : ; ^ % 8 T se

    • maximumPoolSize:Integer.MAX_VALUE

    • keepAliveTime:DEFAULT_KEEPAL$ M z F B C U u _IVE_MILLIS

    • workQueue:new DelayedWorkQueue()

L b Q个类肥壕在实践项目中没用过,阿里的规约中{ # x W 2 R :也是不提倡大家这样运用的。原因= 5 J _ p嘛我觉得可能是:

  1. 经过 ThreadPoolExecutor 的办法,这样的处理办法让写的[ 5 u同学更加清晰y j ; e z线程池的运转规则,躲避资源耗尽的风险。
  2. Executors 供给的办法可能会存在各种反常问题。比如 newFixedThread, ( ? 1 pPool 和 newSingleThreadExecutor 运用的是无界行列,堆积的等候使命可能会导致 OOM;nf 9 P 8 E ) U T @ewCachedThreadPool 和 newScheduledThreadPool 线程数最大数l 4 ( – b o e S 8是 Integer.MAX_VALUE,也可能会导致 OOM。

总结

  1. Executory I x 8 结构的几个中心类有 ExecutorExecutorServiceAbstractExecutorx ) w 3 Y : 5ServiceThreadPoolExecuZ : v u 9tor(关于这几个类的依赖能够看上面的依赖图)ExecutorExecutorService接口界说了 execute()、submit() 办法。Abstv , Q yract/ g m 4 @ 9 fExecutorService抽象类运用模板办法,供给了回来 Future 的 submv S lit() 办法;ThreadPoolExecutor是中心线o a 8 { % !程类,完结 execute() 使命提交的详细逻辑。
  2. Executors 是静态工厂类,能够创建不同类型的线程池。可是不主张在{ 2 Y C 2 g实践项目中运用,由于假如运用不当有可能会形成 OOM

这篇水文没有涉及到太多线程池相关的常识和详细源码,只是非常简略的梳理了一下 Executor 下这几个常见类的联系和l W O Q @ –实践的作用。有关线程池的详细的作业流程、还有 Future 盯梢异步之类的常识点,肥壕会在后续温习的m h y U r i q D c时候一块梳理总结。噢,还有一个常识点:线程池使命反常的捕获,之% W e h f M , t ,前在这儿踩过一个坑,后边也会一并分享出来。

完~