JAVA之Future

97 阅读3分钟

「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。

1、Runnable与Callable

JDK1.0版本: Runnable

public class RunnableDemo {
    public static void main(String[] args) {
        Runnable task = () -> {
            System.out.println("任务执行了");
        };
        new Thread(task).start();
    }
}

Runnable接口的实例对象用来表示工作内容,将它传递给线程,让线程完成这项工作。Runable对象可以作为方法参数传递,可以被放入到队列中,可以跨越网络传递,也可以被保存至文件中。然后,这样的Runnable对象不论被传递到哪台计算机中的哪个线程中,都可以运行。这时,可以将Runbable接口看作GoF的Command模式。

JDK1.5版本:Future和Callable
public class FutureTest {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Callable<Integer> callable = () -> {
            // 代表耗时任务
            Thread.sleep(60000);
            System.out.println("输出结果:1");
            return 1;
        };
        FutureTask<Integer> task = new FutureTask<>(callable);
        new Thread(task).start();
        while (!task.isDone()) {
            try {
                task.get(2, TimeUnit.SECONDS);
            } catch (TimeoutException e) {
                System.out.println("任务尚未完成");
            }
        }
        System.out.println("任务已完成");
    }

Future在英语中有“期货”的意思,买卖双方签订合约,约定在将来某一个时间进行货物的交割。 同理,假设有一个方法需要花费很长时间才能获取运行结果,那么与其一直等结果,不如先拿一张“提货单”。获取提货单并不耗费时间,这里的“提货单”就称为Future。 获取Future角色的线程会在稍后使用Future角色来获取运行结果。如果运行结果已经出来了,那么直接领取即可;如果运行结果还没有出来,那么需要等待结果出来。 java.util.concurrent.Callable接口将“有返回值的某种处理的调用”抽象化了。Callable接口声明了call方法。call方法与Runnable接口的run方法相似,不同的是call方法有返回值。

对比RunnableCallable
方法返回值没有返回值有返回值
能否抛出受检异常不能可以
在Thread类中使用可以不可以
在ExecutorService中使用可以可以
意义表示线程的工作内容表示线程的工作内容

2.Future实现

111.png

Future表示异步计算的结果,提供了一些方法来检查计算是否完成,等待其完成以及取回计算结果。计算完成后,只能使用get方法获得结果,必要时阻塞直到计算准备好为止。通过执行cancel方法来取消计算。提供了其他方法用来确定任务是正常完成还是被取消了。

FutureTask包装器是一种非常便利的机制,可将Callable转换成Future和Runnable,它同时实现二者的接口,代表了可取消的异步计算。

outcome字段代表计算结果

runner字段代表执行这个任务的线程

waiters则是一个类似AQS中等待队列的单向链表,保存因为等待计算结果而休眠的线程;

callable字段代表某种处理的调用

还有有一个state字段,代表这个FutureTask实例的生命周期中的某个状态:

112.png

FutureTask的生命周期,最初为NEW。运行状态仅在set、setException和cancel方法中转换为终态。在完成期间,状态可能会处于瞬时态COMPLETING(正在设置结果时)或者INTERRUPTING(中断执行者以满足cancel(true)时)。

下面沿着FutureTask的生命周期变化结合代码分析:

实例化一个FutureTask时,状态为NEW

// 将Callable实例包装为FutureTask
public FutureTask(Callable<> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    // 状态初始化为NEW
    this.state = NEW;
}

线程启动,执行FutureTask的run方法:

// 将Callable实例包装为FutureTask
public void run() {
    // 1.状态不为NEW或者runner字段不为NULL,任务已经被其他线程执行或取消,直接返回
    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 {
	        // 2.1执行相关计算,并获取结果
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
	        // 3.任务执行异常,设置该异常为返回结果
                setException(ex);
            }
            if (ran)
		// 2.2设置结果
                set(result);
        }
    } finally {
        runner = null;
        // 重新检查状态,避免泄露中断
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}
	
// 2.2设置结果
protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
	// 设置终态为NORMAL
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
	// 清理因为get而被阻塞的线程
        finishCompletion();
    }
}

// 3.设置异常为返回值
protected void setException(Throwable t) {
    // 3.1设置状态为瞬时态COMPLETING
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
    // 设置结果为所抛出异常
        outcome = t;
	// 设置终态为异常
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL);
        // 清理因为get而被阻塞的线程
        finishCompletion();
    }
}

其他线程调用cancel方法取消任务

public boolean cancel(boolean mayInterruptIfRunning) {
    // 计算已设置结果或者状态已被其他线程修改,则取消失败
    if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {
	// 如果可以在运行中中断,则中断该线程,并设置状态为TERRUPTED
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally { // final state
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
	// 清理因为get而被阻塞的线程
        finishCompletion();
    }
    return true;
}

最后是获取计算结果的get方法:

public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,TimeoutException {
    if (unit == null)
        throw new NullPointerException();
    int s = state;
    // 1.任务还没结果且等待指定时间后还没有结果,则抛出超时异常
    if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
        throw new TimeoutException();
    // 2.成功获得结果
    return report(s);
}    
// 2.获得结果
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也有一个类似AQS中的等待队列,休眠的线程以单向链表的形式组织起来:

static final class WaitNode {
    // 调用get方法的线程
    volatile Thread thread;
    // 后继节点
    volatile WaitNode next;
    WaitNode() { thread = Thread.currentThread(); }
}
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)
            Thread.yield();
	// 还未初始化WaitNode节点
        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);
    }
}

// 任务完成、取消、异常后,移除并唤醒所有等待线程
private void finishCompletion() {
    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;
        }
    }
    // 自定义实现,可以在方法实现中查询status已确定这个task是否已经被取消
    done();
    callable = null;
}