首先介绍一下线程池体系

Java.util.concurrent.Executor 负责线程的使用与调度的根接口

ExecutorService:Executor的子接口,线程池的主要接口

AbstractExecutorService:实现了ExecutorService接口,
基本实现了ExecutorService其中声明的所有方法,另有添加其他方法

ThreadPoolExecutor:继承了AbstractExecutorService,主要的常用实现类

ScheduledExecutorService:继承了ExecutorService,负责线程调度的接口

ScheduledThreadPoolExecutor:继承了ThreadPoolExecutor同时实现了ScheduledExecutorService

Executors

我们先来看一下java.util.concurrent.Executors这个类以及和这个类相关的一些借口和抽象类。
Executeor是一个最顶层的接口,它包含有一个execute方法,
这个方法可以接受Runnable对象为参数的方法,其方法签名为void executor(Runnable command)
Executede的所有已知子接口:ExecutorService,ScheduledExecutorService
Executede的所有已知的实现类:AbstractExecutorService,ForkJoinPool,ScheduledThreadPoolExecutor,ThreadPoolExecutor

如有更详细了解Executors请参考官方文档https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html

下面是类图:
Java并发编程之线程池的相关用法

Executors工厂类

通过Executors提供四种线程池,newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor、newScheduledThreadPool。

1.public static ExecutorService newFixedThreadPool(int nThreads)
创建固定数目线程的线程池,以共享的无界队列方式来运行这些线程。

2.public static ExecutorService newCachedThreadPool()
创建一个可缓存的线程池,调用execute将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线 程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。

3.public static ExecutorService newSingleThreadExecutor()
创建一个单线程化的Executor。

4.public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

这里仅列出了一小部分常用的方法,如有需求请参考Java8文档官网,这里附上链接https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html

ExecutorService

所有已知子接口:
ScheduledExecutorService的

所有已知的实现类:
AbstractExecutorService,ForkJoinPool,ScheduledThreadPoolExecutor,ThreadPoolExecutor

公共接口ExecutorService 
扩展了Executor
一Executor,提供方法来管理终端和方法,可以生产出Future为跟踪一个或多个异步任务执行。
一个ExecutorService可以被关闭,这将导致其拒绝新任务。提供了两种不同的方法来关闭		
ExecutorService。该shutdown() 方法将允许先前提交的任务在终止之前执行,而该shutdownNow()方法
可防止等待任务启动并尝试停止当前正在执行的任务。终止时,执行程序没有正在执行的任务,没有
等待执行的任务,也没有任何新任务可以提交。ExecutorService应关闭未使用的以允许回收其资源。


submit方法Executor.execute(Runnable)通过创建和返回Future 可用于取消执行和/或等待完成的方法来
扩展基本方法。方法invokeAny并invokeAll执行最常用的批量执行形式,执行一组任务,然后等待
至少一个或全部完成。(类ExecutorCompletionService可用于编写这些方法的自定义变体。)

ExecutorService是一个比Executor使用更广泛的子类接口,其提供了生命周期管理的方法,返回 Future 对象,以及可跟踪一个或多个异步任务执行状况返回Future的方法;可以调用ExecutorService的shutdown()方法来平滑地关闭 ExecutorService,调用该方法后,将导致ExecutorService停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另一类是还没有开始执行的),当所有已经提交的任务执行完毕后将会关闭ExecutorService。因此我们一般用该接口来实现和管理多线程。
通过 ExecutorService.submit() 方法返回的 Future 对象,可以调用isDone()方法查询Future是否已经完成。当任务完成时,它具有一个结果,你可以调用get()方法来获取该结果。你也可以不用isDone()进行检查就直接调用get()获取结果,在这种情况下,get()将阻塞,直至结果准备就绪,还可以取消任务的执行。Future 提供了 cancel() 方法用来取消执行 pending 中的任务。
更多介绍请参考官方文档:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html

ScheduledExecutorService

所有超级接口:
执行者,ExecutorService

所有已知的实现类:
的ScheduledThreadPoolExecutor

公共接口ScheduledExecutorService
扩展了ExecutorService
的ExecutorService,它可安排命令以给定的延迟后运行,或定期地执行。
这些schedule方法创建具有各种延迟的任务,并返回可用于取消或检查执行的任务对象。该scheduleAtFixedRate和 scheduleWithFixedDelay方法创建并执行定期运行,直到取消的任务。

使用Executor.execute(Runnable) 和方法提交的命令的调度请求延迟为零。方法中也允许零延迟和负延迟(但不包括句点),并将其视为立即执行的请求。 ExecutorService submitschedule

所有schedule方法都接受相对延迟和句点作为参数,而不是绝对时间或日期。将表示为a的绝对时间转换为Date所需形式是一件简单的事情。例如,要在某个未来安排date,您可以使用:schedule(task, date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS)。但请注意,相对延迟的到期不需要与Date由于网络时间同步协议,时钟漂移或其他因素而启用任务的当前电流一致。

的Executors类提供此包中提供的ScheduledExecutorService的实现方便工厂方法。

用法示例
这是一个带有方法的类,该方法将ScheduledExecutorService设置为每隔十秒钟发出一小时的哔声:

 import static java.util.concurrent.TimeUnit.*;
 class BeeperControl {
   private final ScheduledExecutorService scheduler =
     Executors.newScheduledThreadPool(1);

   public void beepForAnHour() {
     final Runnable beeper = new Runnable() {
       public void run() { System.out.println("beep"); }
     };
     final ScheduledFuture<?> beeperHandle =
       scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS);
     scheduler.schedule(new Runnable() {
       public void run() { beeperHandle.cancel(true); }
     }, 60 * 60, SECONDS);
   }
 }

ScheduledExecutorService方法摘要

修饰符和类型 方法和描述
ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit)
创建并执行在给定延迟后变为启用的ScheduledFuture。

ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
创建并执行在给定延迟后启用的一次性操作。

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
创建并执行一个周期性操作,该操作在给定的初始延迟后首先启用,然后在给定的时间段内启用; 执行将在initialDelay那initialDelay+period之后开始 ,然后 initialDelay + 2 * period,依此类推。

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
创建并执行一个周期性动作,该动作在给定的初始延迟之后首先被启用,并且随后在一次执行的终止和下一次执行的开始之间给定延迟。

如想了解更多请参考官方文档https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html

下面是转载部分,转载自:http://www.cnblogs.com/javanoob/p/threadpool.html,介绍了关于阿里发布的 Java开发手册中强制线程池不允许使用 Executors 去创建线程池的原因,以及为什么要用ThreadPoolExecutor 去创建线程池的建议

最近看阿里的 Java开发手册,上面有线程池的一个建议:

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,
这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

结合最近面试的经历,发现这条建议还是十分有用的,因为自己经常使用Executors提供的工厂方法创建线程池,所以忽略了线程池内部的实现。
特别是拒绝策略,面试被问到两次,因为使用Executors创建线程池不会传入这个参数而使用默认值所以我们常常忽略这一参数,还好因为这条建议,自己提前熟悉了一下ThreadPoolExecutor。

ThreadPoolExecutor
先看看如何使用ThreadPoolExecutor创建线程池:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) 

corePoolSize - 线程池核心池的大小。
maximumPoolSize - 线程池的最大线程数。
keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit - keepAliveTime 的时间单位。
workQueue - 用来储存等待执行任务的队列。
threadFactory - 线程工厂。
handler - 拒绝策略。

关注点1 线程池大小
线程池有两个线程数的设置,一个为核心池线程数,一个为最大线程数。
在创建了线程池后,默认情况下,线程池中并没有任何线程,等到有任务来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法
当创建的线程数等于 corePoolSize 时,会加入设置的阻塞队列。当队列满时,会创建线程执行任务直到线程池中的数量等于maximumPoolSize。

关注点2 适当的阻塞队列
java.lang.IllegalStateException: Queue full
方法 抛出异常 返回特殊值 一直阻塞 超时退出
插入方法 add(e) offer(e) put(e) offer(e,time,unit)
移除方法 remove() poll() take() poll(time,unit)
检查方法 element() peek() 不可用 不可用

ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
DelayQueue: 一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue: 一个不存储元素的阻塞队列。
LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列。

关注点3 明确拒绝策略
ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常。 (默认)
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

说明:Executors 各个方法的弊端:
1)newFixedThreadPool 和 newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至 OOM。
2)newCachedThreadPool 和 newScheduledThreadPool:
主要问题是线程数最大数是 Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至 OOM。

Executors
让我们再看看Executors提供的那几个工厂方法。

newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。
此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

new ThreadPoolExecutor(1, 1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue())
newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,
那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。
此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue());

相关文章: