1.Future、FutureTask
Future接口表示异步的结果计算,提供了检查计算是否完成、等待其完成以及获取计算结果的方法。提供了其他方法来确定任务是否正常完成或取消。
FutureTask是Future接口的实现类。
public class MyFuture {
public static void main(String[] args) {
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "test";
}
});
new Thread(futureTask).start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
以上程序的运行结果为:
调用get()方法可以获取FutureTask类中的callable的call()方法的执行结果。如果call()方法还没执行完,get()方法会等待call()方法执行完。也可以使用get(long timeout, TimeUnit unit)来在指定时间内获取结果,如果没有获取会抛出TimeoutException。
下面是get()方法的源码
/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
可以看到get()方法中如果任务没有完成,会执行awaitDone()方法,在awaitDone()方法中,会在for循环中一直判断,如果没有完成就调用LockSupport.park(this)(参考之前的LockSupport的使用https://blog.csdn.net/zhangcjsyl/article/details/84546985)
/**
* 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)
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);
}
}
而在FutureTask的run()方法中,会执行FutureTask类中的callable对象的call()方法,并将call()方法的返回值result作为参数执行set(result);
public void run() {
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);
}
}
在set()方法中将result赋给coutcome,最后执行finishCompletion();
/**
* Sets the result of this future to the given value unless
* this future has already been set or has been cancelled.
*
* <p>This method is invoked internally by the {@link #run} method
* upon successful completion of the computation.
*
* @param v the value
*/
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
finishCompletion()方法会执行LockSupport.unpark(t);这个t就是执行当前任务的线程类,然后执行了LockSupport.park(this)后处于等待的get()方法的线程就会被唤醒
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
}
而get()方法的返回值,是report(s)的结果,在report()方法中,如果这个线程没有被取消,就返回outcome的值,而outcome的值正是在FutureTask的run()方法中执行完call()方法的结果result。
/**
* Returns result or throws exception for completed task.
*
* @param s completed state value
*/
@SuppressWarnings("unchecked")
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);
}
由此可见,FutureTask、Callable也是依赖于Thread、Runnable而实现的,只不过在FutureTask的run()方法中会调用callable的call()方法,因此我们要执行任务的具体实现就可以直接重写call()方法了。
2.线程池
先来一段看似简单的代码:
public class MyExecutor {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(10);
Future<String> future = es.submit(new Callable<String>(){
@Override
public String call() throws Exception {
return "test ThreadPool";
}
});
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
执行结果如下
以上代码的就是通过Executors类的newFixedThreadPool(int) 方法新建一个线程池es,然后用一个Future对象接收es.submit()的返回值,打印出future.get()。
下面来看源码了解线程池是怎么工作的。
首先看newFixedThreadPool(int) 这个方法:
/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
这个方法返回了一个线程池对象,nThreads是线程池中线程的数量。再看这个ThreadPoolExecutor类的构造方法:
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and default thread factory and rejected execution handler.
* It may be more convenient to use one of the {@link Executors} factory
* methods instead of this general purpose constructor.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
在这个构造方法中会调用另一个构造方法:
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
下面是构造参数的说明:
corePoolSize | 核心线程数,默认情况下核心线程会一直存活。 |
maximumPoolSize | 线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。 |
keepAliveTime | 非核心线程的闲置超时时间,超过这个时间就会被回收。当设置allowCoreThreadTimeOut(true)时,线程池中核心线程空闲时间达到keepAliveTime也将关闭。 |
unit | 时间单位 |
workQueue | 阻塞任务队列
|
threadFactory | 创建新线程的线程工厂 |
handler | 当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理
|
然后我们再找submit()的具体实现,在ThreadPoolExecutor类中没有submit()方法,往上找到ThreadPoolExecutor类的父类AbstractExecutorService类中找到了submit()方法:
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
在这个方法中,将参数封装成一个RunableFuture对象,然后执行execute()方法,因为是在ThreadPoolExecutor类中调用的submit()方法,所以我们还是要在ThreadPoolExecutor找这个execute()方法:
/**
* Executes the given task sometime in the future. The task
* may execute in a new thread or in an existing pooled thread.
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
* the task is handled by the current {@code RejectedExecutionHandler}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution
* @throws NullPointerException if {@code command} is null
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* 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 call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, 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.get();
if (workerCountOf(c) < corePoolSize) {
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(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
这里看到了一个ctl的变量,是ThreadPoolExecutor类中的一个成员变量,在ThreadPoolExecutor类中可以看到下面一些成员属性和方法:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
这里我着重介绍一下这几个重要的成员变量:
COUNT_BITS:int位数减三,也就是29;
CAPACITY:1左移29位减1,用二进制表示就是0001 1111 1111 1111 1111 1111 1111 1111;
RUNNING :-1左移29位,用二进制表示就是1110 0000 0000 0000 0000 0000 0000 0000;
SHUTDOWN:0左移29位,还是0;
STOP:1左移29位,用二进制表示就是0010 0000 0000 0000 0000 0000 0000 0000;
TIDYING:2左移29位,用二进制表示就是0100 0000 0000 0000 0000 0000 0000 0000;
TERMINATED:3左移29位,用二进制表示就是0110 0000 0000 0000 0000 0000 0000 0000;
从注释和命名上可以知道,RUNNING 、SHUTDOWN、STOP、TIDYING、TERMINATED都是表示线程池运行状态,而ctl表示的是线程池当前的状态(状态和运行状态是我自己取的名字,也不知道怎么叫比较好),初始值是RUNNING 和0取逻辑或,也就是RUNNING 的值。
方法runStateOf(int c)返回c和~CAPACITY取逻辑与,我们可以知道~CAPACITY就是RUNNING ,RUNNING 前三位是1,那么返回结果中前三位就是c的二进制的前三位,RUNNING 后29位是0,那么返回结果中的后29位就是0。也就是说得到的结果是c的前三位加29个0,也就是c代表的运行状态值。
方法workerCountOf(int c)返回c和CAPACITY取逻辑与,CAPACITY前三位是0,得到的结果的前三位也就是0,CAPACITY后29位是1,得到的结果就是c的后29位,最终的结果也就是将c的前三位置0的值。也就是c这个状态下运行的线程数量。
总结一下,一个int数表示线程池现在状态,这个状态包括线程池的运行状态和线程池中线程的数量。这个int数的二进制前三位表示运行状态,后29位表示线程的数量。runStateOf()方法可以求出这个状态的运行状态,workerCountOf()方法可以求出这个状态的线程数量。(这一块看了好久才懂,果然是写jdk的大佬,打死我也写不出这样的代码。。。)
上面的五个运行状态值也可以表示运行状态,也就是当前线程池中的线程数量为0。
我们还是看到刚才的execute()方法,顾名思义execute()方法就是执行一个task,execute()方法的参数只要是Runnable就行,但如果是Futrue类型则可以异步地获得结果。ctl是一个AtomicInteger对象,get()方法就是取其对应的int值。如果当前线程池中的线程数量少于核心线程数量的话就执行addWorker()方法,这个方法是用来判断是否可以添加一个线程,方法的第二个参数core表示是否核心线程,true就是是否可以添加一个核心线程,false表示是否可以增加非核心线程,如果可以增加,就会将当前线程池中的线程数量加一。源码如下:
/**
* Checks if a new worker can be added with respect to current
* pool state and the given bound (either core or maximum). If so,
* the worker count is adjusted accordingly, and, if possible, a
* new worker is created and started, running firstTask as its
* first task. This method returns false if the pool is stopped or
* eligible to shut down. It also returns false if the thread
* factory fails to create a thread when asked. If the thread
* creation fails, either due to the thread factory returning
* null, or due to an exception (typically OutOfMemoryError in
* Thread.start()), we roll back cleanly.
*
* @param firstTask the task the new thread should run first (or
* null if none). Workers are created with an initial first task
* (in method execute()) to bypass queuing when there are fewer
* than corePoolSize threads (in which case we always start one),
* or when the queue is full (in which case we must bypass queue).
* Initially idle threads are usually created via
* prestartCoreThread or to replace other dying workers.
*
* @param core if true use corePoolSize as bound, else
* maximumPoolSize. (A boolean indicator is used here rather than a
* value to ensure reads of fresh values after checking other pool
* state).
* @return true if successful
*/
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
c是线程池的状态,rs是取的线程池的运行状态,如果rs大于等于SHUTDOWN并且不满足(rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())的话就返回false,也就是,线程池的运行状态为RUNNING或者满足(rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())会继续执行。继续执行会进入一个循环,如果当前线程池的线程数量大于CAPACITY(0001 1111 1111 1111 1111 1111 1111 1111,因为前三位是表示状态,后29位是数量,所以不能大于这个值)或者大于线程池的线程数量限制(如果core为true取核心线程池数量,如果core为false就取线程池的最大数量),就返回false。如果当前线程池没有达到数量限制,就会执行compareAndIncrementWorkerCount(c),这个方法如果返回true,就跳出循环,否则会一直循环。下面是compareAndIncrementWorkerCount()方法飞源码:
/**
* Attempts to CAS-increment the workerCount field of ctl.
*/
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
这个方法就是比较ctl与传入参数的大小,如果相等就给ctl+1并返回true,不相等就不操作并返回false,compareAndSet()方法是一个原子性操作。也就是说,如果别线程修改了ctl的值,在addWorker()方法中会进入下一次循环重新取ctl的值,确保ctl中表示的当前线程数量是准确的。
在成功将ctl的数量加一之后,先定义了workerStarted和workerAdded两个boolean变量,然后新建一个worker对象,Worker类中的thread属性是用其构造参数Runnable构造的一个Thread对象,接着在加锁条件下判断是否可以添加这个线程,可以的话在线程池的workers这个HashSet中添加这个Worker对象,将workerAdded置为true。然后启动线程t,并将workerStarted置为true,最后返回workerStarted。如果execute()的参数是Futrue类型,则会将结果保存到Future的outcome中,调用get()即可获得结果。