前语
肥壕最近在温习线程这一块常识, Executor
、ExecutorService
、ThreadPoolExecutor
这三兄弟总感觉很难辨认,每次看完后没过多久又会忘,所以今日特地来盘一下 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 接口,界说了推迟履行和周期履行的办法
-
ScheduledThreadPoolExecutor:V 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 ^ Hnable 和 Callable 实例的参数。
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 S呢f 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 [一样,不同的是
- 等候行列中的使命不l 6 V T B # | I会履行,并直接回来这些等候履行& P ] W 3 d的. 5 { h 5 a ,使命
- 测验中止现在履行的使命,调用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嘛我觉得可能是:
- 经过 ThreadPoolExecutor 的办法,这样的处理办法让写的[ 5 u同学更加清晰y j ; e z线程池的运转规则,躲避资源耗尽的风险。
- 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。
总结
- Executory I x 8 结构的几个中心类有
Executor
、ExecutorService
、AbstractExecutorx ) w 3 Y : 5Service
、ThreadPoolExecuZ : v u 9tor
(关于这几个类的依赖能够看上面的依赖图)Executor
、ExecutorService
接口界说了 execute()、submit() 办法。Abstv , Q yract/ g m 4 @ 9 fExecutorService
抽象类运用模板办法,供给了回来 Future 的 submv S lit() 办法;ThreadPoolExecutor
是中心线o a 8 { % !程类,完结 execute() 使命提交的详细逻辑。 - 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 ,前在这儿踩过一个坑,后边也会一并分享出来。
完~