并发编程之FutureTask的实现解析

简介

        很多时候我们希望创建一个新线程去执行一个功能相对独立的任务,并在任务完成之后返回执行结果。如果实现Runnable接口,没有办法获得返回值;如果实现Callable接口,必须使用ExecutorService来执行,不能使用更简单灵活的new thread方法来实现。为了解决上面两个问题,于是有了FutureTask。FutureTask实现了Runnable, Future<V>,所以它既可以通过new thread来跑任务,也可以通过ExecutorService来管理任务,同时FutureTask还提供了超时返回的功能。另外还有一个非常有用的功能是它提供了一个可重载方法done(),done()会在任务执行完之后被调用,用户可以override该方法,来执行一些数据清理,句柄释放等操作。下面来看下FutureTask的具体实现。

 

FutureTask实现原理

API接口

接口接口描述
boolean cancel(boolean mayInterruptIfRunning)取消任务,参数表示是否取消正在执行的任务
boolean isCancelled()任务在正常结束前是否被取消
boolean isDone();任务是否执行完
V get()返回任务结果
V get(long timeout, TimeUnit unit)带超时时间的任务返回方法

实现原理

跟concurrent包中大多数并发类的设计一样,FutureTask也是基于AQS设计了一个内部类Sync来实现FutureTask的功能。Sync的通过传入Callable<V> callable来构造一个对象,这个callable就是需要执行的任务,V是任务返回的类型。Sync通过AQS的state属性来管理任务状态,比如1是RUNNING,2是RAN,4是CANCELLED。下面分别来剖析FutureTask的API的实现。

 

   1.isCancelled()

public boolean isCancelled() {
        return sync.innerIsCancelled();
    }
    真正的实现是通过sync的innerIsCancelled()来实现的,可以看出AQS的state属性值是保存任务状态的。

 

   

 boolean innerIsCancelled() {
            return getState() == CANCELLED;
        }
 
2.isDone()
public boolean isDone() {
        return sync.innerIsDone();
    }
     真正的实现是通过sync的innerIsDone()实现的,判断state的状态是RAN或者CANCELLED,并且这个任务的执行线程是否为null。
boolean innerIsDone() {
            return ranOrCancelled(getState()) && runner == null;
        }

 private boolean ranOrCancelled(int state) {
            return (state & (RAN | CANCELLED)) != 0;
        }
 
3.cancel(boolean mayInterruptIfRunning)
public boolean cancel(boolean mayInterruptIfRunning) {
        return sync.innerCancel(mayInterruptIfRunning);
    }
    取消任务的方法是Sync的innerCancel(boolean mayInterruptIfRunning)来实现的:
boolean innerCancel(boolean mayInterruptIfRunning) {
	    for (;;) {
		int s = getState();
		if (ranOrCancelled(s))
		    return false;
		if (compareAndSetState(s, CANCELLED))
		    break;
	    }
            if (mayInterruptIfRunning) {
                Thread r = runner;
                if (r != null)
                    r.interrupt();
            }
            releaseShared(0);
            done();
            return true;
        }
    这个方法通过轮询的方式 ,采用CAS(compareAndSet)的同步方式来把state状态设置为CANCELLED。如果设置参数需要中断正在执行的任务,则调用interrupt()来中断任务。最后通过tryReleaseShared释放执行线程。并且执行done()方法来执行一些清理操作。FutureTask的done()方法没有执行让任何操作,用户可以通过extends的方法来override该方法。tryReleaseShared的方法很简单,仅仅把执行线程runner设置为null。
 
4.public V get()
public V get() throws InterruptedException, ExecutionException {
        return sync.innerGet();
    }
  获取任务的返回值,该方法委托Sync的innerGet()来实现。
V innerGet() throws InterruptedException, ExecutionException {
            acquireSharedInterruptibly(0);
            if (getState() == CANCELLED)
                throw new CancellationException();
            if (exception != null)
                throw new ExecutionException(exception);
            return result;
        }
      acquireSharedInterruptibly方法是AQS里面非常重要的一个方法,他以轮询的方式来阻塞线程,直到获得中断或者满足退出轮询条件来终结果阻塞,运行后面的代码。acquireSharedInterruptibly的退出阻塞状态的条件是tryAcquireShared方法的返回值大于0,tryAcquireShared方法是需要用户来override的。在这里tryAcquireShared返回大于0的条件是任务是否完成或取消。
protected int tryAcquireShared(int ignore) {
            return innerIsDone()? 1 : -1;
        }
  如果acquireSharedInterruptibly捕获中断状态,则抛出中断异常,否则执行下面的代码,如果状态为CANCELLED,则抛出CancellationException;如果Sync的exception属性不为空,则抛出这个exception。如果到这里innerGet还没退出,说明任务是执行完的,则返回任务结果。
 
5.public V get(long timeout, TimeUnit unit)
public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        return sync.innerGet(unit.toNanos(timeout));
    }
  带超时时间的获取任务结果方法,委托sync的innerGet(long nanosTimeout)来执行:
V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException {
            if (!tryAcquireSharedNanos(0, nanosTimeout))
                throw new TimeoutException();
            if (getState() == CANCELLED)
                throw new CancellationException();
            if (exception != null)
                throw new ExecutionException(exception);
            return result;
        }
  这个方法与上面讲的不带超时时间的方法差不多,只是tryAcquireSharedNanos超时之后会抛出超时异常。
 
6.public void run()
public void run() {
        sync.innerRun();
    }
  启动任务运行方法,委托sync的innerRun()来实现。
void innerRun() {
            if (!compareAndSetState(0, RUNNING))
                return;
            try {
                runner = Thread.currentThread();
                if (getState() == RUNNING) // recheck after setting thread
                    innerSet(callable.call());
                else
                    releaseShared(0); // cancel
            } catch (Throwable ex) {
                innerSetException(ex);
            }
        }
  首先通过CAS方法把state更新为RUNNING状态,如果更新失败,说明这个任务的状态不是初始状态(0),说明这个任务被取消了,或者运行完了,或者正在运行。直接退出方法。只有state能更新为RUNNING状态,则说明能够开始执行任务。状态更新RUNNING之后,任务真正执行之前,需要在检查下状态是否为RUNING。因为这个反复没有加锁,状态可能被更新为取消了。所以如果检查状态不在为RUNNING,则释放线程资源,否则把runner设置为当前线程,并通过innerSet(callable.call());执行用户的任务,然后设置任务状态。
void innerSet(V v) {
	    for (;;) {
		int s = getState();
		if (s == RAN)
		    return;
                if (s == CANCELLED) {
		    // aggressively release to set runner to null,
		    // in case we are racing with a cancel request
		    // that will try to interrupt runner
                    releaseShared(0);
                    return;
                }
		if (compareAndSetState(s, RAN)) {
                    result = v;
                    releaseShared(0);
                    done();
		    return;
                }
            }
        }
  如果状态已经运行结束了 直接退出;如果状态设置为CANCELLED,则强制释放线程并试图中断正在执行的任务并退出。如果既没运行完也没取消,则通过CAS把状态设置为RAN,把结果设置为call()返回的结果,然后释放线程(runner属性设置为null),并执行done()操作。
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值