一、Runnable和Callable
Runnable
是一个接口,只声明了一个run()
方法,此方法为void类型,所以只能执行无返回值的任务。
public interface Runnable {
public abstract void run();
}
Callable是java.util.concurrent包下的一个接口,提供了一个call()方法:
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
该方法返回的数据类型就是传进来的泛型类型,主要用于计算产生结果。如果没有计算出结果,则会抛出异常。
二、Future
Future
则是对于具体的Runnable
或Callable
任务的执行结果进行取消、判断是否完成、获取任务执行结果。
Future
接口声明了5个方法:
boolean cancel(boolean mayInterruptIfRunning);
尝试取消此任务的执行。如果取消成功则返回true,取消失败则返回false。
如果任务已完成、已取消或由于其他原因无法取消,则此尝试将失败,返回false。
传入的参数如果为true,则直接中断正在执行的任务,返回true;如果为false,则允许正在进行的任务完成。
boolean isCancelled();
任务是否被取消成功,如果此任务在正常工作之前被取消,则返回true。
boolean isDone();
如果此任务已完成,则返回true。完成可能是由于正常终止、异常或取消 - 在所有这些情况下,此方法将返回 true。
V get() throws InterruptedException, ExecutionException;
以阻塞的方式获取任务执行结果,知道任务执行完毕返回。
另外在以下情况会抛出异常:
1.任务被取消:
抛出 CancellationException
异常
2.计算引发异常:
抛出 ExecutionException
异常
3.当前线程被中断:
抛出 InterruptedException
异常
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
同上,但规定了超时时间,如果在规定时间内没有计算出结果,会抛出TimeoutException
异常。
三、FutureTask
FutureTask类实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future接口,可知FutureTask可做Runnable和Future一样的事情。
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
3.1 首先看FutureTask的成员变量
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;
private Callable<V> callable;
private Object outcome; // non-volatile, protected by state reads/writes
private volatile Thread runner;
private volatile WaitNode waiters;
1.state
:state保存了FutureTask任务的状态值,为volatile
类型,保证了原子性,即任何一个线程修改了state的值,都可以保证其他线程看到修改后的值。
state共有如下几个取值(对应的值依此为0到6):
NEW
:为state的初始值
。表示该任务是个新任务。
COMPLETING
:当前任务已经执行完毕或执行任务过程中出现了异常,但结果尚未保存到outcome(outcome用来保存计算结果或者是异常信息)中,属于中间状态
。
NORMAL
: 任务执行完毕,且结果保存到outcome中。此为最终状态
。
EXCEPTIONAL
: 任务执行过程中出现异常,且异常信息已保存到outcome中。此为最终状态
。
CANCELLED
:任务尚未开始或任务执行时调用了cancel(false)
方法。此为最终状态
。
INTERRUPTING
:任务尚未开始或任务执行时调用了cancel(true)
方法。此为中间状态
。
INTERRUPTED
: 在调用cancel(true)时,会调用:
Thread t = runner;
if (t != null)
t.interrupt();
state会变为INTERRUPTED
,此为最终状态
。但如果被终端的线程正在sleep()、wait()、或join(),则会抛出InterruptedException()异常,因此cancel(true)
(注意:中间状态时间短暂,不代表任务仍在执行,而是任务执行完毕后尚未设置执行结果,因此除了NEW状态,其余状态都代表已经执行结束)
可能出现的转变状态为:
NEW -> COMPLETING -> NORMAL
NEW -> COMPLETING -> EXCEPTIONAL
NEW -> CANCELLED
NEW -> INTERRUPTING -> INTERRUPTED
2.callable
:要执行的task任务。
3.outcome
: 保存执行结果或出现异常时保存异常信息
4.runner
:执行task的线程,在取消和中断线程时需要知道是哪个线程。
5.waiters
:为WaitNode
的对象。WaitNode
为一个简单的单向链表结构的队列,当任务执行过程中,有线程想要调用get()获取结果时,则该线程会被阻塞加入WaitNode
,直到该任务执行完毕。
以下为WaitNode
的结构:
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
仅由一个Thread和指向下一个节点的next组成,thread即为当前线程。其本质是一个Treiber栈。
3.2静态代码块中初始化需要CAS操作的属性的偏移量
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long stateOffset;
private static final long runnerOffset;
private static final long waitersOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> k = FutureTask.class;
stateOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("state"));
runnerOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("runner"));
waitersOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("waiters"));
} catch (Exception e) {
throw new Error(e);
}
}
CAS操作调用的是compareAndSwap***
方法
3.3 构造方法
3.3.1
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
将传入的callable赋值给上面说的成员变量callable,将state初始化为NEW;
3.3.2
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
传入Runnable对象,通过Executors.callable()方法将runnable转为callable保存给callable变量,同时会返回传入的result。
将runnable转为callable的方法如下:
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
这里调用RunnableAdapter()
适配,代码如下:
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
}
RunnableAdapter
实现了Callable接口,在call()中调用task.run(),将传入的result返回。
3.4 方法
3.4.1 run()
public void run() {
//代码1
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
//代码2
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()
//代码3
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
代码1:
1.state!=NEW: 如果state不为NEW,即任务已经执行完毕或被取消,直接返回。
2. 若state是NEW,则尝试将当前线程保存到runner
中,即如果runner为空就将当前线程保存给runner,若runner不为空,则说明已经有线程在执行,直接return,否则进入try-catch。
代码2:
1.定义V result
保存callable的执行结果,置ran为true,代表执行成功。
2.若出现异常,则在catch中捕获,将result置为null,ran置为false代表出现异常,同时通过setException(ex)
设置异常。
setException(ex)
代码如下:
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
此方法中,尝试将state的值从NEW改为COMPLETING
,如果成功,则将异常信息保存到outcome中,然后再将状态改为最终态EXCEPTIONAL
,最后调用finishCompletion()
移除并唤醒WaitNode中所有阻塞等待的线程,调用done()
,并让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;
}
- 如果ran为true,即正常执行,通过
set(result)
,set()源码如下:
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
在set()中,尝试将状态从NEW变为COMPLETING
中间态(这里再重新理解上面说的state的7个状态会更深刻一点),并将结果保存给outcome
,接着将state由COMPLETING
转为NORMAL
最终状态,同上调用 finishCompletion()
。
代码3:
1.在finally
中重新将runner
置空,表明当前线程运行完毕,在下一个线程进入到run()
(代码1)方法时,便可通过CAS重新将线程保存到runner
。
2.若ran为false,即出现了异常,则调用handlePossibleCancellationInterrupt(state)
处理异常,
private void handlePossibleCancellationInterrupt(int s) {
// It is possible for our interrupter to stall before getting a
// chance to interrupt us. Let's spin-wait patiently.
if (s == INTERRUPTING)
while (state == INTERRUPTING)
Thread.yield(); // wait out pending interrupt
// assert state == INTERRUPTED;
// We want to clear any interrupt we may have received from
// cancel(true). However, it is permissible to use interrupts
// as an independent mechanism for a task to communicate with
// its caller, and there is no way to clear only the
// cancellation interrupt.
//
// Thread.interrupted();
}
在该方法中,如果状态一直是INTERUPING
,则一直进行yield()。
3.4.2 get()
线程可调用get()方法获取任务执行的结果,如果当前任务尚未执行完毕,则阻塞调用get()方法的线程,直到任务执行结束。
/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
get()
方法中首先判断当前的状态是否小于等于COMPLETING
,即任务是否处于尚未开始或尚未结束或结果尚未保存到outcome的状态,如果是,则调用awaitDone(false, 0L);
阻塞等待,否则调用report(s)返回执行结果。
report()
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);
}
获取当前结果outcome
,如果当前状态为NORMAL
,则直接返回结果,如果当前状态大于等于CANCELLED
,则抛出CancellationException()
异常,其他状态则抛出ExecutionException()
异常。
3.4.3 awaitDone()
线程调用此方法阻塞等待任务执行结束。
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
//若开启超时,则计算截止时间
final long deadline = timed ? System.nanoTime() + nanos : 0L;
//创建一个WaitNode节点
WaitNode q = null;
//是否进入阻塞队列
boolean queued = false;
for (;;) {
//代码1
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
//代码2
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
//代码3
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
//代码4
else if (q == null)
q = new WaitNode();
//代码5
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
//代码6
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
方法的参数为 timed(是否开启超时时间)和nanos(若开启超时时间,则此参数为超时时间)
(代码1到代码6在一个无限for循环中,不停的进行循环判断)
代码1:
如果当前线程被中断,则将阻塞队列waitNode中的该节点给移除,并抛出InterruptedException()
异常。
代码2:
判断当前阻塞线程的状态是否大于COMPLETING
,如果大于,则该任务已经执行完毕,如果当前节点非空,则删除该节点的thread,并返回状态。
代码3: 如果当前状态为COMPLETING
,则说明任务执行完毕,但是执行结果尚未赋值给outcome
,此时执行yield
让出CPU。
代码4:如果当前阻塞等待节点q为空,则新建一个节点。
**代码5:**如果尚未入队列,则将上一个节点的next指向waiters,然后尝试让新的节点q替换waiters,如果尝试成功则queued为true,否则为false。
**代码6:**如果开启了超时限制,则重新计算等待时间,如果 nanos<=0L,即超时了,则移除等待队列中的该节点,并返回当前state;如果没有超时,则继续阻塞重新计算后的时间。如果没有开启超时限制,则直接阻塞等待直到被唤醒。
3.4.4 cancel()
public boolean cancel(boolean mayInterruptIfRunning) {
//代码1
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
//代码2
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
//代码3
finishCompletion();
}
return true;
}
代码1:
1.如果state
不等于NEW
,则任务已经执行完毕或出现异常或被取消,直接返回false
;
2.如果任务尚未开始,则通过CAS尝试将状态从NEW
改为mayInterruptIfRunning
,若mayInterruptIfRunning
为true
,代表需要中断任务执行线程,则改为INTERRUPTING
中间状态,否则改为CANCELLED
最终状态,如果尝试更新成功,则返回true
,否则返回false
。
代码2:
如果进行了中断,则获取当前的线程runner,中断之。最后将状态由INTERRUPTING
改为INTERRUPTED
代码3:
执行finishCompltion()
(任务出现异常或正常执行完毕或成功取消任务都会调用该方法,上文已提到),返回true
3.4.5 isCancelled()
public boolean isCancelled() {
return state >= CANCELLED;
}
判断任务是否被取消。只要状态大于等于CANCELLED都返回true。
3.4.6 isDone()
public boolean isDone() {
return state != NEW;
}
只要不是NEW,都返回true,代表任务已完成。
4.Callable+Future示例
package com.wml.test2.future;
import java.util.concurrent.*;
/**
* @author MaoLin Wang
* @date 2020/1/2723:18
*/
public class FutureDemo {
public static class Task implements Callable<Integer>{
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable线程开始执行任务");
Thread.sleep(1500);
for (int i = 0; i < 50; i++) {
if (Thread.currentThread().isInterrupted()){
System.out.println("Callable线程计算任务中断");
return null;
}
if (i%2==0){
sum=sum+i;
System.out.println("sum="+sum);
}
}
System.out.println("Callable线程执行完毕 计算结果为"+sum);
return sum;
}
}
public static void main(String[] args) {
Task task=new Task();
ExecutorService pool = new ThreadPoolExecutor(6,100,0L,TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
Future<Integer>future=pool.submit(task);
pool.shutdown();
try {
Thread.sleep(3000L);
System.out.println("主线程执行其他任务中.....");
if (future.get()!=null){
System.out.println("主线程获取Callable执行结果:"+future.get());
}else {
System.out.println("尚未获取到结果");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
结果:
Callable线程开始执行任务
sum=0
sum=2
sum=6
sum=12
sum=20
sum=30
sum=42
sum=56
sum=72
sum=90
sum=110
sum=132
sum=156
sum=182
sum=210
sum=240
sum=272
sum=306
sum=342
sum=380
sum=420
sum=462
sum=506
sum=552
sum=600
Callable线程执行完毕 计算结果为600
主线程执行其他任务中.....
主线程获取Callable执行结果:600
五、Callable+FutureTask示例
Task类同上
public static void main(String[] args) {
ExecutorService pool = new ThreadPoolExecutor(6,100,0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());
Task task=new Task();
//创建FutureTask任务
FutureTask futureTask=new FutureTask(task);
pool.submit(futureTask);
pool.shutdown();
try {
Thread.sleep(3000L);
System.out.println("主线程执行其他任务中.....");
if (futureTask.get()!=null){
System.out.println("主线程获取Callable执行结果:"+futureTask.get());
}else {
System.out.println("尚未获取到结果");
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
结果:
Callable子线程开始计算!
sum=0
sum=1
sum=3
sum=6
sum=10
sum=15
sum=21
Cancel.................
sum=28
Callable子线程计算任务中断!
(如有错误还请指正~感谢)