文章目录
观看《Java并发编程的艺术》所做笔记
Executor框架
Executor简介
JDK5开始把线程的工作单元和执行机制分离开来
Executor是将工作单元和执行机制分离的线程池
- 工作单元(任务): Runnable,Callable
- 执行机制: Executor框架
hotsopt中的线程一一映射为OS线程
使用Executor的模型
表面(上层): Executor框架来控制任务调度
实际(下层): OS内核来为线程分配可用CPU
Executor结构
-
任务:Runnable,Callable
-
任务的执行:核心接口:Executor,ExecutorService(Executor的子类) , 核心实现类:ThreadPoolExecutor,ScheduledThreadPoolExecutor(ExecutorService的实现类)
-
异步计算的结果: 接口Future以及实现Futurn的类(比如: FutureTask)
整体结构图
使用Executor整体流程
- 创建任务 Runnable或Callable
- 将任务交给
ExecutorService
执行 - 操作结果Future
Runnable任务执行过程
public class ExecutorRunnableTask {
public static void main(String[] args) {
//1.创建任务
Runnable task = ()->{
System.out.println("执行Runnable任务");
};
//2.将任务交给ExecutorService执行 (Executors是工厂稍后介绍)
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(task);
//3.通过get()查看任务结果 或 cancel()取消任务 (只能是Future任务)
executorService.shutdown();
}
}
Callable任务执行过程
public class ExecutorCallableTask {
public static void main(String[] args) {
Callable<String> task = ()->{
System.out.println("执行Callable任务");
return "Callable任务有返回值";
};
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> future = executorService.submit(task);
// 如果执行cancel(true) 会取消执行任务 执行get方法会抛出异常
// System.out.println(future.cancel(true));
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
Callable任务只能使用submit方法
Runnable任务可以使用submit或execute方法
只有submit方法才有返回值Future
执行FutureTask任务
public class ExecutorFutureTask {
public static void main(String[] args) {
FutureTask task = new FutureTask<String>(()->{
System.out.println("执行FutureTask任务");
return "完成";
});
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<?> future = executorService.submit(task);
try {
System.out.println(future.get());//null
System.out.println(task.get());//完成
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
}
因为FureTask间接实现了Runnable接口,并且可以通过构造器获得runnable或callable任务,所以可以直接交给ExecutorService执行
ThreadPoolExecutor详解
重要组件
ThreadPoolExecutor的重要组件
-
corePoolSize核心线程数: 线程池基本线程数量
-
maximumPoolSize最大线程数 线程池允许创建的最大线程数
-
workQueue任务队列 保持等待执行任务的阻塞队列
-
RejectedExecutionHandler 拒绝策略 当线程不够用,阻塞队列也满了的时候的一种策略(有4种策略)
-
keepAliveTime超时时间和unit时间单位 工作线程空闲后存活的时间
-
threadFactory线程工厂 线程工厂可以给每个创建出来的线程设置有意义的名字
Executors是工厂类,用来创建线程池
Executors可以创建3种ThreadPoolExecutor: SingleThreadExecutor,FixedThreadPool,CachedThreadPool
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
SingleThreadExecutor
SingleThreadExecutor源码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(
//核心线程=1 最大线程=1
1, 1,
//等待时间0
0L, TimeUnit.MILLISECONDS,
//使用无界(Integer.MAX_VALUE)链表阻塞队列
new LinkedBlockingQueue<Runnable>()));
}
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
通过源码可以知道SingleThreadExecutor
使用无界阻塞队列
无界阻塞队列带来的影响
- 线程池中总线程数不会超过核心线程数 因为一但超过就会把任务放进阻塞队列中
- 最大线程数,等待时间,饱和(拒绝)策略都是无效参数因为阻塞队列是无界的,所以不会饱和
SingleThreadExecutor流程图
- 线程数量少于核心线程数时,创建新的工作线程执行任务
- 线程数量为核心线程数1时,将任务放入阻塞队列
- 线程执行完任务,反复从阻塞队列中获取任务执行
使用场景
单个线程的SingleThreadExecutor适用于需要顺序的执行各个任务,并在任意时间点不存在多线程活动的场景
FixedThreadPool
FixedThreadPool源码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
//核心线程数=最大线程数=通过构造器设置
nThreads, nThreads,
//无等待时间关闭
0L, TimeUnit.MILLISECONDS,
//无界链表阻塞线程
new LinkedBlockingQueue<Runnable>());
}
流程图
- 线程数<核心线程数,创建新工作线程执行任务
- 线程数 = 核心线程数,将任务放入阻塞队列中
- 线程执行完任务,反复去阻塞队列中获取任务执行
使用场景
FixedThreadPool是一个使用固定线程的线程池,适用于满足资源需求管理,限制当前线程数量的场景,负载较重的服务器
CacheThreadPool
源码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(
//核心线程数=0 ,最大线程数=最大值
0, Integer.MAX_VALUE,
//当线程数大于核心线程数时,线程空闲超过60s就会销毁线程
60L, TimeUnit.SECONDS,
//放一个取一个(不存储元素)的阻塞队列
new SynchronousQueue<Runnable>());
}
SynchronousQueue队列
offer:将任务放入阻塞队列
poll:从阻塞队列中取出任务
每一个offer都要和poll对应
流程图
- 将任务offer阻塞队列中,如果有空闲线程在poll,就会把任务交给这个空闲线程执行
- 没有空闲线程执行poll,就拿不到任务,所以会创建新工作线程执行任务
- 线程执行poll 等待阻塞队列中的任务 线程空闲60s就会被终止
长时间空闲的CacheThreadPool不占用资源
使用场景
CacheThreadPool是大小无界的线程池,采用SynchronousQueue,适用于执行很多短期异步任务,负载较轻
ScheduledThreadPoolExecutor详解
ScheduledThreadPoolExecutor主要用来执行定时任务
使用流程
使用方式
public class ScheduledThreadPoolTest {
public static void main(String[] args) {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
//scheduleAtFixedRate 固定频率执行任务,周期起点为开始执行任务
/*scheduledThreadPoolExecutor.scheduleAtFixedRate(()->{
System.out.println("Scheduled任务执行");
//初始延迟:10s 周期:1s
},10,1, TimeUnit.SECONDS);*/
//scheduledWithFixedDelay 固定延迟执行任务,周期起点为任务结束
scheduledThreadPoolExecutor.scheduleWithFixedDelay(()->{
System.out.println("Scheduled任务执行");
//初始延迟:10s 周期:1s
},10,1, TimeUnit.SECONDS);
scheduledThreadPoolExecutor.shutdown();
}
}
有2个方法的使用方式
- scheduleAtFixedRate: 固定频率执行任务,周期起点为开始执行任务(如果执行任务时间以及超过周期时间那么执行完任务就会开始执行下一次任务)
- scheduledWithFixedDelay: 固定延迟执行任务,周期起点为任务结束
查看scheduledWithFixedDelay源码
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay <= 0)
throw new IllegalArgumentException();
//将runnable任务封装为实现了RunnableScheduledFuture接口的ScheduledFutureTask定时任务
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
//延迟执行任务
delayedExecute(t);
return t;
}
运行图
- 通过scheduleAtFixedRate或scheduledWithFixedDelay方法将runnable任务封装为实现了RunnableScheduledFuture的ScheduledFutureTask定时任务,再将这个定时任务放入DelayQueue
- 线程池中的线程从DelayQueue中获取ScheduledFutureTask定时任务执行
实现机制
DelayQueue阻塞队列
ScheduledThreadPoolExecutor采用DelayQueue阻塞队列
DelayQueue是一个支持延迟获取的无界阻塞队列,DelayQueue使用PriorityQueue(优先级队列)排序,队列中的元素必须实现Delayed接口(实现getDelay方法:返回当前时间到目标时间的剩余时间)
ScheduledFutureTask定时任务
ScheduledThreadPoolExecutor会把runnable任务封装为ScheduledFutureTask
ScheduledFutureTask有3个重要字段
//任务在ScheduledThreadPoolExecutor中的序号
private final long sequenceNumber;
//任务具体执行时间(触发时间)
private long time;
//周期时间
private final long period;
time越小就会排在DelayQueue队列的前面时间早的任务先被拿到执行
time相同时,序号小的先执行 时间相同时,先放进队列的任务先执行
分析源码查看如何封装ScheduledFutureTask
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay <= 0)
throw new IllegalArgumentException();
//主要看这段================================================
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
//得到触发时间
triggerTime(initialDelay, unit),
//得到周期
unit.toNanos(-delay));
//=========================================================
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
ScheduledFutureTask(Runnable r, V result, long ns, long period) {
super(r, result);
this.time = ns;
this.period = period;
//序号原子自增
this.sequenceNumber = sequencer.getAndIncrement();
}
//super(r, result);源码
public FutureTask(Runnable runnable, V result) {
//使用工厂类Executors把Runnable任务包装为Callable任务
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
线程执行周期任务流程图
- 获取到期任务(time大于当前时间) take()
- 执行任务
- 修改下次执行该任务的触发时间 修改time
- 将任务放回队列中 add()
查看步骤1获取任务源码 take()
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
//获取lock
lock.lockInterruptibly();
try {
for (;;) {
//拿到队列第一个任务(time最小,最先要执行的任务)
RunnableScheduledFuture<?> first = queue[0];
//如果任务为空则等待
if (first == null)
available.await();
else {
//delay:获得当前时间到触发时间剩余时间 单位纳秒
long delay = first.getDelay(NANOSECONDS);
//delay负数则取出任务(会有下滤操作,维护优先级队列排序顺序)
if (delay <= 0)
return finishPoll(first);
//delay正数则等待剩余时间
first = null; // don't retain ref while waiting
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
//唤醒线程
available.signal();
//释放lock
lock.unlock();
}
}
-
获取lock
-
获取周期任务
如果队列为空,线程去condition中等待
如果触发时间未到,则在condition中等待剩余时间
如果触发时间以到,则取出任务
-
释放lock 队列还有任务则唤醒线程
步骤2在循环中执行,只有取出任务去执行才会退出
查看将任务返回队列中源码 add()
public boolean add(Runnable e) {
return offer(e);
}
public boolean offer(Runnable x) {
if (x == null)
throw new NullPointerException();
RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;
final ReentrantLock lock = this.lock;
//获取lock
lock.lock();
try {
//如果队列大小不够就扩容50%
int i = size;
if (i >= queue.length)
grow();
size = i + 1;
//如果只有一个任务则设置该任务下标为0 否则进行下滤siftUp
if (i == 0) {
queue[0] = e;
setIndex(e, 0);
} else {
siftUp(i, e);
}
//如果这个任务在队头则唤醒线程去获得
if (queue[0] == e) {
leader = null;
available.signal();
}
} finally {
//释放lock
lock.unlock();
}
return true;
}
FutureTask详解
简介
FutureTask实现了Future接口和Runnable接口,用来做异步计算结果
FutureTask不仅可以接收Runnable任务还可以接收Callable任务 FutureTask通过构造器获得Runnable或Callable任务
如果接收的是Runnable任务则会使用工厂类Executors转为Callable任务
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
状态
根据FutureTask是否执行run方法可以分为三种状态: 未启动 , 已启动 , 已完成
- 未启动: 执行前
- 已启动: 执行中
- 已完成: 执行后正常结束 或 执行过程中抛出异常 或 被取消 cannel方法
FutureTask状态和get,cannel方法的关系
状态 | get | cancel |
---|---|---|
未启动 | 阻塞调用线程 | 任务不会执行 |
已启动 | 阻塞调用线程 | cancel(false):不会对正在执行任务的线程产生影响 cancel(true):中断执行任务线程 |
已完成 | 获得结果 (或抛出异常) | 返回false |
实现
FutureTask内部维护了WaitNode链表 等待状态的线程链表
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
使用
public static void main(String[] args) {
FutureTask task = new FutureTask<String>(()->{
System.out.println("执行FutureTask任务");
return "完成";
});
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<?> future = executorService.submit(task);
try {
System.out.println(task.isDone());//false
//执行FutureTask任务
System.out.println(future.get());//null
System.out.println(task.get());//完成
System.out.println(task.isDone());//true
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
源码中FutureTask状态
/* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
//未启动
private static final int NEW = 0;
//已启动
private static final int COMPLETING = 1;
//正常完成
private static final int NORMAL = 2;
//异常
private static final int EXCEPTIONAL = 3;
//被取消
private static final int CANCELLED = 4;
//打断 中断
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
查看get源码
public V get() throws InterruptedException, ExecutionException {
//得到状态
int s = state;
//如果状态是NEW或COMPLETING 未启动或已启动 则等待 完成任务会唤醒
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
private V report(int s) throws ExecutionException {
//得到结果 (可能是异常信息)
Object x = outcome;
//如果状态是正常结束的则返回结果
if (s == NORMAL)
return (V)x;
//如果状态是被取消或中断 抛出CancellationException
if (s >= CANCELLED)
throw new CancellationException();
//其他情况抛出ExecutionException 结果是异常信息
throw new ExecutionException((Throwable)x);
}
查看cancel源码
public boolean cancel(boolean mayInterruptIfRunning) {
//如果 状态不是NEW 直接返回false
//如果 状态是NEW 根据参数更新状态 成功返回false
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
//如果要中断执行任务线程 中断线程并且将状态更新为中断
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
//唤醒WaitNode链表上的线程,置空callable
finishCompletion();
}
return true;
}
查看run源码
public void run() {
//状态不是NEW 直接返回
//将这个任务的运行线程null更新为当前线程 更新失败 直接返回
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
//结果
V result;
//是否成功执行任务
boolean ran;
try {
//执行任务
result = c.call();
//设置成功执行任务
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
//设置异常
setException(ex);
}
//如果成功执行任务则设置结果
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
//任务被中断则处理中断
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
//设置异常
protected void setException(Throwable t) {
//如果将NEW状态更新为COMPLETING状态 则...
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//将异常信息赋值给结果
outcome = t;
//更新状态为EXCEPTIONAL
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
//唤醒WaitNode链表线程,置空callable
finishCompletion();
}
}
//设置结果
protected void set(V v) {
//如果将NEW状态更新为COMPLETING状态 则...
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//将结果赋值给结果outcome
outcome = v;
//更新状态为NORMAL
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
//唤醒WaitNode链表线程,置空callable
finishCompletion();
}
}