成员分为四个部分:任务、任务执行、任务执行结果以及任务执行工具类java
任务:实现Callable
接口或Runnable
接口面试
任务执行部分:ThreadPoolExecutor
以及ScheduledThreadPoolExecutor
多线程
任务执行结果:Future
接口以及FutureTask
实现类框架
任务执行工厂类:Executors
异步
ThreadPoolExecutor(核心)ide
ThreadPoolExecutor
一般使用Executors
进行建立,其内部含有三种线程池:函数
FixedThreadPool
:含有固定线程数的线程池。工具
SingleThreadExecutor
:单线程的线程池,须要保证任务顺序执行时采用。post
CachedThreadPool
:大小无界的线程池,只要须要线程就能够一直建立线程。this
ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor
一般使用Executors
进行建立,其内部含有两种线程池:
ScheduledThreadPoolExecutor
:含有固定线程数的定时任务线程池SingleThreadScheduledExecutor
:只包含一个线程数的定时任务线程池,须要保证任务顺序执行时采用。提交任务给任务执行框架执行后,任务执行框架会返回一个Future
接口类型的对象,该对象内部包含了任务执行结果信息。
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<> submit(Runnable task)
复制代码
在JDK1.8返回的是FutureTask
对象,但不必定返回的就是FutureTask
对象,只保证返回Future
接口。
实现Runnable
或Callable
都可被任务执行框架执行,它们之间的区别是实现Runnable
的任务执行完成后不会返回结果,而实现Callable
接口的任务能够返回结果。
返回结果为null的封装(没有什么意义)
public static Callable<Object> callable(Runnable task) 复制代码
拥有返回结果的封装
public static <T> Callable<T> callable(Runnable task, T result) 复制代码
上一篇讲过ThreadPoolExecutor构造函数中的参数含义,这里再也不赘述,若是忘记了或者还没阅读过,能够先去了解一下,两篇文章是衔接的:juejin.im/post/5e9c45…
主要参数有4个:
corePolSize
:核心线程池大小maximumPoolSize
:最大线程池大小BlockingQueue
:存储未执行任务的阻塞队列RejectedExecutionHandler
:任务没法被执行时的拒绝策略fixedThreadPool
称为可重用固定线程数的线程池,下面是构造该线程池的方法源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
复制代码
参数解读
corePoolSize
和maximumPoolSize
都设置为用户传入的nThreads
。keepAliveTime
设置为0L
,表明只要空闲线程闲下来就立刻被终止,若是设置为SECONDS > 0
,则表明线程空闲下来后等待新任务的最长时间SECONDS
。TimeUnit
表明keepAliveTime
的单位workQueue
设置阻塞队列execute()执行流程
curThread
表明核心线程池的当前线程数
若是 curThread < corPoolSize
,那么建立新线程执行任务
若是 curThread = corPoolSize
,那么将任务加入阻塞队列当中
线程池中的线程执行完任务后会空闲下来,而后会一直从阻塞队列中获取任务执行
使用无界阻塞队列LinkedBlockingQueue
的后果:
curThread = corPoolSize
后,任务会一直加入到阻塞队列,严重时阻塞队列的任务数过多会形成GC内存溢出。maximumPoolSize
是一个无效参数。只有当任务没法加入到阻塞队列时知足curThread < maximumPoolSiize
才会在线程池中建立新的线程执行该任务。keepAliveTime
参数无效curThread > maximumPoolSize
,也就不会出现任务拒绝执行的状况,不会调用任务拒绝方法。GC内存溢出示例(我的理解,若是有错,欢迎指出!):
public class Task implements Runnable {
private String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name);
}
}
public class FixedThreadPoolTest {
private static final ExecutorService fixThreadPool = Executors.newFixedThreadPool(10);
private static int i = 0;
public static void main(String[] args) {
// 不停地往线程池中提交任务
for (;i < 100000;i++) {
fixThreadPool.execute(new Task("任务" + i));
}
fixThreadPool.shutdown();
}
}
复制代码
设置堆内存参数:-Xms2m -Xmx2m
抛出OOM异常,缘由在于阻塞队列中的任务数过多。
使用单个Worker
线程的Executor
。本质上是fixedThreadPool
的corePoolSize
和maximumPoolSize
都设置为1
,此外无其它区别。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
复制代码
参数解释
corePoolSize
和MaximumPoolSize
的参数都设置为1
,其它参数含义和默认值都和fixedThreadPool
相同。fixedThreadPool
相同。execute()执行流程
Worker
线程来执行任务根据须要建立新线程的线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
复制代码
参数解释
corePoolSize
设置为0,即corePool
为空maximumPoolSize
设置为Integer.MAX_VALUE
,因此maximumPool
是几乎无界的,可容纳足够多的线程keepAliveTime
设置为60L,每一个空闲线程能够等待新任务的时间是60秒TimeUnit
单位设置为SECONDS
表明以秒为单位计算SynchronousQueue
,每一次的offer
操做都要对应一个poll
操做execute()执行流程
offer
进阻塞队列中poll
出任务执行,不然执行第3步poll
出任务执行。因为JDK1.6与JDK1.8中延时队列的实现不一样,因此在阅读本小节时不要死记硬背其实现,掌握其思想。图的组件均会用中文表示,对思想的掌握有帮助,实现的思想是一致的。
该类用于指定特定的延迟时间后运行任务,或者按期执行任务。Timer
是单线程的,该类是多线程的,并且该类功能更增强大。
ScheduledThreadPoolExecutor将任务提交到延时队列当中存储,当任务到达执行时间时,Worker
工做线程会拉取任务进行执行,空闲的线程会一直阻塞,不断尝试获取延时队列中的任务。
ScheduledThreadPoolExecutor
本质上是ThreadPoolExecutor
的改造:
调用线程把待调度的任务(ScheduledFutureTask
)放入一个延时队列当中,ScheduledFutureTask
的主要成员变量有三个:
time
:该任务要被执行的具体时间sequenceNumber
:该任务被添加到ScheduledThreadPoolExecutor
的序号,标记任务添加到队列的时刻period
:任务执行的间隔周期延时队列中采用优先级队列进行排序。排序时,time
越小排在越前面,time
相同时,sequenceNumber
越小排在越前面。
ScheduledThreadPoolExecutor
执行任务的流程以下图:
time
>= 当前时间time
变量为下次要被执行的时间表示异步计算的结果,实现了Runnable、Future接口。
FutureTask的状态转换图以下所示
FutureTask.run()
当任务处于未启动状态时,调用FutureTask.run()会使任务执行。
FutureTask.get()
FutureTask处于未启动或已启动状态时,执行FutureTask.get()会致使线程阻塞;
当FutureTask处于已完成状态时,调用FutureTask.get()会当即返回结果或者抛出异常。
FutureTask.cancel()
当任务处于未启动状态时,调用FutureTask.cancel()会致使此任务永远不会被执行;
当任务处于已启动状态时,执行FutureTask.cancel(true)会中断任务,执行FutureTask.cancel(false)方法将不会对正在执行此任务的线程产生任何影响;
当FutureTask处于完成状态时,执行FutureTask.cancel(...)方法将返回false。
get()和cancel()的状态转换图:
当一个线程须要等待另外一个线程把某个任务执行完后才能继续执行,可使用FutureTask。这种用法并非很常见,只要知道有这种用法就能够了。
这一章主要和上一章的线程池有很大联系,阅读到这里很是不容易,收获也应该不少,留下几道面试题来检验本身是否真的掌握了这些内容吧。
若是这篇文章能给你带来一点点的小收获,你的一个小小的点赞我会开心一成天!感谢你的阅读!