JAVA Future类详解及Thread线程是如何运行Future类的

一、Future基本介绍

        Future(java.util.concurrent Interface Future<V>)表示异步计算的结果。Future接口提供了检查计算是否完成、检查计算是否被取消、等待计算完成并获取计算结果等方法。

        在并发编程中,我们经常用到非阻塞的模型,但继承thread类和实现runnable接口,都无法保证获取到之前的执行结果。而通过实现Callback接口,并用Future可以来接收多线程的执行结果。

        Future表示一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后作出相应的操作。

二、Future使用方法

public static void main(String[] args) throws ExecutionException, InterruptedException {
    Callable call = new Callable<String>(){
        @Override
        public String call() throws Exception {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "我被调用";
        }
    };
    FutureTask<String> future = new FutureTask<String>(call);
    new Thread(future).start();
    System.out.println(future.get());
}

        返回结果“我被调用” 

        接下来我们就围绕这这段代码看看整体的运行流程。

        我们这里主要关注的类是Callable FutureTask两个类

三、Callable类

         Callable是个支持泛型的函数式接口,里面有个call()方法

四、FutureTask继承体系

        RunnableFuture是个组合接口,继承了Future和Runnable两个接口,FutureTask实现了RunnableFuture接口,所以FutureTask拥有Future和Runnable两个接口的功能,我们知道Runnable接口是个函数式接口,里面只有一个run方法,我们重点看下Future这个类

五、Future类接口说明

/**
 * 方法可以用来停止一个任务,如果任务可以停止(通过mayInterruptIfRunning来进行判断),则可以返回true,
 * 如果任务已经完成或者已经停止,或者这个任务无法停止,则会返回false
 * 注意:mayInterruptIfRunning不一定会影响取消,要看具体实现,concurrent包里面部分类是不受该参数影响的,如CompletableFuture
*/
boolean cancel(boolean mayInterruptIfRunning);

/**
 * 任务是否已经取消
 */
boolean isCancelled();

/**
 * 任务是否已经完成
 */
boolean isDone();

/**
 * 获取任务返回值,未执行完成则会阻塞等待结果
 */
V get() throws InterruptedException, ExecutionException;

/**
 * 指定超时时间,规定时间未获取结果则抛出TimeoutException
 */
V get(long timeout, TimeUnit unit)
	throws InterruptedException, ExecutionException, TimeoutException;

六、FutureTask的主要实现

        我们知道我们调用了Thread的start()方法之后线程进入就绪状态,在线程获取运行资源之后会调用run()方法,所以我们就从这里入手,看看FutureTask的run()方法:

public void run() {
	if (state != NEW ||
		!RUNNER.compareAndSet(this, null, Thread.currentThread()))
		return;
	try {
	    // 这里是初始化我们传入的callable对象
		Callable<V> c = callable;
		if (c != null && state == NEW) {
			V result;
			boolean ran;
			try {
				// 调用callable的call方法并获取返回结果
				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);
	}
}

        由上面的代码我们可以看到run()方法在执行的时候调用了我们传入的Callable对象的call()方法,并在成功执行后获取返回结果并对返回结果做封装,接下来我们看看封装返回结果的set()方法:

protected void set(V v) {
	if (STATE.compareAndSet(this, NEW, COMPLETING)) {
        // 将返回结果传到outcome参数中
		outcome = v;
		STATE.setRelease(this, NORMAL); // final state
		finishCompletion();
	}
}

        FutureTask将Callable的call()方法的运行的返回结果传到outcome参数中,至此这个调用过程也结束了,我们看看父类Future的get方法的实现是如何获取返回参数的:

public V get() throws InterruptedException, ExecutionException {
	int s = state;
	if (s <= COMPLETING)
        // 这里阻塞等待结果,可以跟进去看看,里面有个for循环一直等待结果
		s = awaitDone(false, 0L);
	return report(s);
}

        跟进去report()方法:

private V report(int s) throws ExecutionException {
	Object x = outcome;
	if (s == NORMAL)
		// 返回outcome对象
		return (V)x;
	if (s >= CANCELLED)
		throw new CancellationException();
	throw new ExecutionException((Throwable)x);
}

        至此,我们就跟完了整个调用流程了。

猜你喜欢

转载自blog.csdn.net/m0_37798046/article/details/130368908
今日推荐