前言
上一章讲述了线程池ThreadPoolExecutor的原理。在线程池的运算过程结束后可以有返回值
1. ExecutorService 示例
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(6);
List> list = new ArrayList();
for (int i = 0; i < 6; i++) {
Future future = executorService.submit(()-> Thread.currentThread().getName());
list.add(future);
}
list.stream().forEach((s)->{
try {
System.out.println(s.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
executorService.shutdown();
}
Q:如何获取线程运算的返回值;如果中间某个线程阻塞,会有什么结果?
2. 源码分析
2.1 submit方法分析
public abstract class AbstractExecutorService implements ExecutorService {
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public Future> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public Future submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public Future submit(Callable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
其中RunnableFuture同时具有Runnable, Future的特性。
public interface RunnableFuture extends Runnable, Future {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
可以看出,提交的线程时callable线程,因为runnable线程没有返回值。
当然runnable线程也可以有计算结果,实际是包装成callable接口实现。
2.2 分析核心方法newTaskFor
/**
* Returns a {@code RunnableFuture} for the given callable task.
*
* @param callable the callable task being wrapped
* @param the type of the callable's result
* @return a {@code RunnableFuture} which, when run, will call the
* underlying callable and which, as a {@code Future}, will yield
* the callable's result as its result and provide for
* cancellation of the underlying task
* @since 1.6
*/
protected RunnableFuture newTaskFor(Callable callable) {
return new FutureTask(callable);
}
/**
* Returns a {@code RunnableFuture} for the given runnable and default
* value.
*
* @param runnable the runnable task being wrapped
* @param value the default value for the returned future
* @param the type of the given value
* @return a {@code RunnableFuture} which, when run, will run the
* underlying runnable and which, as a {@code Future}, will yield
* the given value as its result and provide for cancellation of
* the underlying task
* @since 1.6
*/
protected RunnableFuture newTaskFor(Runnable runnable, T value) {
return new FutureTask(runnable, value);
}
引用FutureTask类,实现RunnableFuture类,其实就是一个Runnable接口。
public class FutureTask implements RunnableFuture {
/** The underlying callable; nulled out after running */
private Callable callable;
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Callable}.
*
* @param callable the callable task
* @throws NullPointerException if the callable is null
*/
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Runnable}, and arrange that {@code get} will return the
* given result on successful completion.
*
* @param runnable the runnable task
* @param result the result to return on successful completion. If
* you don't need a particular result, consider using
* constructions of the form:
* {@code Future> f = new FutureTask(runnable, null)}
* @throws NullPointerException if the runnable is null
*/
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
传入了callable私有属性。
其中传入runnable接口需要计算结果的在此传入。Executors.callable(runnable, result)
public static Callable callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter(task, result);
}
/**
* A callable that runs given task and returns given result
*/
static final class RunnableAdapter implements Callable {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
转换成了callable接口,直接返回我们传入的result结果。
这里要注意:定义了线程运行的状态
public class FutureTask implements RunnableFuture {
/**
* The run state of this task, initially NEW. The run state
* transitions to a terminal state only in methods set,
* setException, and cancel. During completion, state may take on
* transient values of COMPLETING (while outcome is being set) or
* INTERRUPTING (only while interrupting the runner to satisfy a
* cancel(true)). Transitions from these intermediate to final
* states use cheaper ordered/lazy writes because values are unique
* and cannot be further modified.
*
* 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;
/** The underlying callable; nulled out after running */
private Callable callable;
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
/** The thread running the callable; CASed during run() */
private volatile Thread runner;
/** Treiber stack of waiting threads */
private volatile WaitNode waiters;
其中callable属性就是我们的线程接口引用
outcome记录运行结果
runner和waiters适用于中间逻辑处理,使用了CAS原子特性
2.3 结果的由来
在我的上一篇分析线程池源码的文章https://blog.csdn.net/fenglllle/article/details/82790242
总结出了,线程池中线程的运作方式
通过factory创建一个新线程,在新线程中,run方法调用我们传入的runnable接口的run方法。
那么在上面分析的FutureTask实现就是我们传入的runnable接口实例。
分析run方法
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//结果的获取就在这里,直接调用call方法
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);
}
}
直接调用result = c.call();获取结果。
set(result);设置结果值。CAS操作
protected void set(V v) {
//更新状态,NEW为COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//对结果进行赋值
outcome = v;
//最终状态,线程运行结束
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
//结束
finishCompletion();
}
}
结束处理
/**
* Removes and signals all waiting threads, invokes done(), and
* nulls out callable.
*/
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
//移除引用
q.thread = null;
//唤醒线程
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
//GC
callable = null; // to reduce footprint
}
看完set结果的代码,我们看获取结果的源码。
2.4 获取结果get方法
public V get() throws InterruptedException, ExecutionException {
int s = state;
//判断状态
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
//超时时间内获取线程结果
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}
awaitDone方法,及其重要方法,等待线程执行结束;中断线程;超时等
/**
* Awaits completion or aborts on interrupt or timeout.
*
* @param timed true if use timed waits
* @param nanos time to wait, if timed
* @return state upon completion
*/
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
//线程运行中,已中断
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
//线程已完成
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
//运行中,有结果还没设置,让出当前线程资源
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
//收集当前线程,状态可能是 运行中/阻塞线程/中断过程的线程
q = new WaitNode();
else if (!queued)
//阻塞结果的当前线程存入futrue对象的waiters
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,q.next = waiters, q);
//限时获取结果处理
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
//超时移除当前线程
removeWaiter(q);
return state;
}
//阻塞当前获取结果的线程--切记不是阻塞运行线程
LockSupport.parkNanos(this, nanos);
}
else
//不设置超时限制,阻塞获取结果的线程
LockSupport.park(this);
}
}
LockSupport.park(this);
阻塞获取结果,这是future的缺陷,前面的线程阻塞会阻塞后面执行的结果的获取。影响效率。
获取判断了线程运行状态,下面看report(int s)
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
NORMAL状态才会有结果
分析了获取结果阻塞的原因,LockSupport.park(this); 由于结果的获取按线程的提交先后顺序,先提交的可能会阻塞后提交的线程的运行结果。
那么来看看JDK1.8的优化方案吧
3. ExecutorCompletionService
3.1 示例解析
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService e = Executors.newFixedThreadPool(6);
ExecutorCompletionService executorService = new ExecutorCompletionService(e);
for (int i = 0; i < 6; i++) {
executorService.submit(() -> Thread.currentThread().getName());
}
for (int i = 0; i < 6; i++) {
System.out.println(executorService.take().get());
}
e.shutdown();
}
简洁很多,下面来看原理
3.2 源码解析
public class ExecutorCompletionService implements CompletionService {
private final Executor executor;
private final AbstractExecutorService aes;
private final BlockingQueue> completionQueue;
看属性,多了一个BlockingQueue,或许这就是有价值的地方。
跟进
/**
* Creates an ExecutorCompletionService using the supplied
* executor for base task execution and a
* {@link LinkedBlockingQueue} as a completion queue.
*
* @param executor the executor to use
* @throws NullPointerException if executor is {@code null}
*/
public ExecutorCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = new LinkedBlockingQueue>();
}
设置了执行器,创建了一个 Integer.MAX_VALUE的单链表队列
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
/**
* Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
*
* @param capacity the capacity of this queue
* @throws IllegalArgumentException if {@code capacity} is not greater
* than zero
*/
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node(null);
}
单链表
/**
* Linked list node class
*/
static class Node {
E item;
/**
* One of:
* - the real successor Node
* - this Node, meaning the successor is head.next
* - null, meaning there is no successor (this is the last node)
*/
Node next;
Node(E x) { item = x; }
}
看submit方法
public Future submit(Callable task) {
if (task == null) throw new NullPointerException();
RunnableFuture f = newTaskFor(task);
executor.execute(new QueueingFuture(f));
return f;
}
newTaskFor方法,跟进后发现跟 2.2 分析核心方法newTaskFor 没区别
private RunnableFuture newTaskFor(Callable task) {
if (aes == null)
return new FutureTask(task);
else
return aes.newTaskFor(task);
}
new QueueingFuture(f),关键点,JDK1.8之前埋下的钩子
/**
* FutureTask extension to enqueue upon completion
*/
private class QueueingFuture extends FutureTask {
QueueingFuture(RunnableFuture task) {
super(task, null);
this.task = task;
}
//重点来了,上面方法的钩子发挥作用
protected void done() { completionQueue.add(task); }
private final Future task;
}
set方法
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
/**
* Removes and signals all waiting threads, invokes done(), and
* nulls out callable.
*/
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
//埋下钩子的地方
done();
callable = null; // to reduce footprint
}
protected void done() { }//默认什么都不干
任务完成加入队列,就不存在阻塞的问题了。这样获取的结果的顺序就不是顺序的了。先执行的线程结果优先写入队列。
获取结果直接队列取值。
4. 总结
ExecutorCompletionService相比ExecutorService,引入链表阻塞队列,解决了线程运算结果获取阻塞的问题。效率提升。
问题依旧:队列长度Integer.MAX,注意内存溢出。