FutureTask源码阅读

151 阅读7分钟

FutureTask简介

  • FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future

  • Runnable接口,可以让FutureTask由线程池执行,也可以直接用线程调用执行

  • Future接口,可以查询任务是否完成,取消任务以及获取结果等操作

  • FutureTask可用于线程池提交任务,任务会被包装成FutureTask,其他线程可以在线程池执行 过程中查询任务状态进而获取结果或者取消任务

FutureTask属性及构造方法

  • 为什么需要runner属性呢? 当需要取消任务时,会给正在执行任务的线程一个中断信号

  • WaitNode:Treiber stack,使用CAS操作栈顶来实现入栈和出栈,实际上是在操作单链表头结点

		
	//当前任务状态
    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 callable;
    
    //正常情况下:保存任务执行结果
    //非正常情况:保存任务抛出的异常
    private Object outcome; // non-volatile, protected by state reads/writes
    
    //当前任务执行期间,保存当前执行任务的线程对象引用
    private volatile Thread runner;
    
    //使用了WaitNode来保存等待get()的线程
    private volatile WaitNode waiters;

    public FutureTask(Callable callable) {
        if (callable == null)
            throw new NullPointerException();
			
        //callable就是任务    
        this.callable = callable;
        
        //设置任务状态为NEW
        this.state = NEW;       // ensure visibility of callable
    }

    public FutureTask(Runnable runnable, V result) {   
	
        //使用装饰者模式将runnable接口转换为callable接口
        //当前任务执行结果为result
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       
    }
	

		
	//thread保存当前等待结果的线程
	static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }
	

UNSAFE

  • compareAndSwapObject(Object var1, long var2, Object var4, Object var5)

  • var1参数是操作的对象,var2参数就是操作字段的偏移量,var4参数就是原值,var5参数就是期望值

  • 这里拿到state, runner, waiters这些字段的偏移量是为了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);
		}
	}	
	

解析run()

  • run()方法是FutureTask被线程执行的入口
		
	//任务执行入口
    public void run() {
    
        //条件一:true:说明当前task已经执行过了,或者被cancel,总之非NEW状态
        //条件二:true:CAS设置runner为当前线程失败,当前任务被其他线程抢占
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
            
        //当前task为new状态,当前线程抢占成功    
        try {
            
            //当前任务
            Callable c = callable;
            
            //条件一:防止提交空任务
            //条件二:防止其他线程已经cancel当前任务
            if (c != null && state == NEW) {
            
                //结果
                V result;
                
                //true:执行成功
                //false:抛出异常
                boolean ran;
                
                try {
                    //执行任务
                    result = c.call();
                    
                    //正常执行
                    ran = true;
                } catch (Throwable ex) {
                
                    //抛出异常,结果null
                    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() 和 setException()

  • 任务执行完成后会调用set()方法设置outcome,出现异常调用setException()设置outcome

  • 会用CAS操作修改任务状态,有可能设置失败,这是因为有其他线程取消了任务

		
	//设置执行结果
    protected void set(V v) {
    
        //使用CAS设置当前任务状态为 完成中
        //失败情况:外部线程取消了任务
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        
            //设置结果
            outcome = v;
            
            //设置当前任务状态为正常完成
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            
            //唤醒阻塞在get()线程,下面会写
            finishCompletion();
        }
    }
    
    //执行出现异常时,执行此方法设置异常
    protected void setException(Throwable t) {
    
        //使用CAS设置当前任务状态为 完成中
        //失败情况:外部线程取消了任务
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        
            //设置结果,结果为异常
            outcome = t;
            
            //设置当前任务状态为异常状态
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
			
			//唤醒阻塞在get()线程,下面会写
            finishCompletion();
        }
    }
	

解析get() 和 report()

  • get()用来获取任务执行的结果,当任务为未执行状态或者正在执行状态时会阻塞在awaitDone()方法
		
	//场景:可能有多个线程等待结果
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        
        //条件成立:未执行状态,正在执行状态
        if (s <= COMPLETING)
            
            //返回状态,可能线程已经睡了一会
            s = awaitDone(false, 0L);
            
        //抛出异常或者正常结果    
        return report(s);
    }
	
	//get()调用这个方法
    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);
    }
	

解析awaitDone() 和 removeWaiter()

  • 这里为了理解方便把Treiber stack理解成一个队列

  • awaitDone()方法会把当前线程封装成WaitNode对象,放入队列中并阻塞

  • removeWaiter()方法把结点移除队列

		
	//get()方法可能调用
    private int awaitDone(boolean timed, long nanos) throws InterruptedException {
        
        //0:不带超时
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        
        //用来封装当前线程的WaitNode对象
        WaitNode q = null;
        
        //当前线程 WaitNode对象是否入队
        boolean queued = false;
        
        //自旋
        for (;;) {
		
            //条件成立:当前线程是被其他线程使用中断唤醒
            //Thread.interrupted():判断中断状态,会把中断标记重置为false
            if (Thread.interrupted()) {
			
                //出队
                removeWaiter(q);
				
                //抛出中断异常
                throw new InterruptedException();
            }
            
            //被正常唤醒,获取最新状态
            int s = state;
            
            //说明已经有结果,可能好可能坏
            if (s > COMPLETING) {
			
                //条件成立:说明线程创建过node
                if (q != null)
                    q.thread = null;
					
                //返回结果
                return s;
            }
             
            //说明任务接近完成,看前面就知道马上就会把状态设置为已完成
            else if (s == COMPLETING) // cannot time out yet
			
                //暂时放弃CPU
                Thread.yield();
                
            //条件成立:第一次自旋,当前线程还未封装成WaitNode    
            else if (q == null)
			
                //把当前线程封装成WaitNode
                q = new WaitNode();
                
            //条件成立:第二次自旋,线程已经封装成WaitNode,但是还未入队
            else if (!queued)
            
                //q.next = waiters:将当前线程的node结点next指向队头结点
                //通过CAS设置队头waiters为当前线程的node结点q
                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
                //park当前线程
                //等待其他线程中断或者唤醒当前线程
                LockSupport.park(this);
        }
    }

    
     //出队操作
    private void removeWaiter(WaitNode node) {
        if (node != null) {
        
            //将node中的thread引用置为空
            node.thread = null;
            retry:
            for (;;) {          // restart on removeWaiter race
			
                //pred:q的前置结点  q:从队头开始遍历  s:q的后置结点
                for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                    s = q.next;
                    
                    //当前结点thread不为空
                    if (q.thread != null)
                        pred = q;
                        
                    //当前结点thread为空,需要出队 //前置结点不为空   
                    else if (pred != null) {
					
                        //直接指向后置结点
                        pred.next = s;
						
                        //前置结点对应的线程可能会被唤醒,也出队,继续自旋 
                        if (pred.thread == null) // check for race
                            continue retry;
                    }
					
                    //当前结点thread为空,前置结点也为为空:也就是出队结点为头结点
					//cas设置头结点
                    else if (!UNSAFE.compareAndSwapObject(this, waitersOffset, q, s))
                        continue retry;
                }
                break;
            }
        }
    }
	

解析cancel()

  • cancel()的前提是当前任务状态为NEW

  • cancel()方法可以直接取消当前任务,也可以给当前正在执行任务的线程发送一个中断信号

		
	//参数true:如果有执行的线程,给它发一个中断信号
    //false:直接取消任务
    public boolean cancel(boolean mayInterruptIfRunning) {
        
        //条件一:state == NEW 当前任务还未执行
        //条件二:CAS 设置状态
        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 {
            finishCompletion();
        }
        return true;
    }
	

解析finishCompletion()

  • finishCompletion()方法是在任务执行完set方法设置了outcome后或者有线程cancel()后调用该方法

  • finishCompletion()方法用来将阻塞的线程一一出队,并唤醒

		
    private void finishCompletion() {
        // assert state > COMPLETING;
        
        //把q指向头结点
        for (WaitNode q; (q = waiters) != null;) {
        
            //使用CAS设置waiters为null
            //失败情况:cancel()方法也会指向finishCompletion()这个方法
            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;
                        
                    //GC    
                    q.next = null; // unlink to help gc
                    
                    //指向下一个结点
                    q = next;
                }
                break;
            }
        }
        
        //可以扩展done()
        done();
        
        //释放任务
        callable = null;        // to reduce footprint
    }
	

总结

  • 下图是线程可能的状态转换图

Markdown

  • 下图总结了几种可能存在的情况,可以结合源码走一下流程

  • 比如线程A正在执行任务,线程B和线程C等待获取结果被阻塞,随后线程C被中断,这时线程C会把自己从Treiber stack中移除,然后抛出中断异常

  • 假如线程D取消任务,线程A在设置结果时,会发现状态不对,设置失败,线程D会唤醒线程B,这时线程B会抛出取消异常

  • 假如线程A顺利执行任务完成并设置结果,会唤醒线程B,这时线程B会拿到正常的结果

Markdown