jdk Future的使用方式

Java 1.5 引入了 Callable 和 Future 接口以解决线程无法返回执行结果的问题。Callable 允许我们在任务中返回结果,而 Future 提供了检查任务状态和获取结果的方法。在示例中,通过 Future 我们可以看到任务是在异步执行的,即使主线程睡眠时间长于任务,Future 仍能正确反映任务的执行情况。这种设计模式提高了多线程环境下的程序设计灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Future是什么

在使用线程的时候,我们会理所当然的想到继承Thread或者实现Runnable,但这两种方式都存在着一个问题,无法返回执行结果。并且,继承Thread也会带来一个破坏面向对象原则的问题,即不符合里氏替换原则。

为了解决此类问题,JDK1.5后引入了FutureCallable

Callable和Future

首先,我们来观察一个Callable接口,它是一个支持泛型并且函数式编程的接口,在使用时我们仅需要实现它的call()方法。
在这里插入图片描述
其次,我们再来看一下Future接口,各方法解释已写入代码注释中。

public interface Future<V> {

    /**
     * 试图中断线程。
     * 1、如果任务已经执行完毕(completed)或者已经被取消(canceled),
     *    又或者是因为一些其他的原因,任务不能被中断,则该方法会执行失败。
     * 2、如果在任务还没启动并且调用该方法成功的情况下,任务将永远无法执行。
     * 3、如果任务已经启动,参数mayInterruptIfRunning可用于决定是否尝试中止任务
     * 4、当该任务返回了,isDone()方法会永远返回true,因为如果该方法返回false,则说明线程已经执行完成或者被中断,
     *    如果返回true,则说明线程被当次调用中止成功了,但不论哪种,线程都是已经停止了的情况。
     * 5、如果方法返回true,则isCanceled()方法返回true。
     */
    boolean cancel(boolean mayInterruptIfRunning);


    boolean isCancelled();

    /**
     * 如果任务完成(不论正常或者异常)则返回true。
     */
    boolean isDone();

    /**
     * 等待任务执行完成,然后获取它的结果
     * @return the computed result
     * @throws CancellationException if the computation was cancelled
     * @throws ExecutionException if the computation threw an
     * exception
     * @throws InterruptedException if the current thread was interrupted
     * while waiting
     */

    V get() throws InterruptedException, ExecutionException;

    /**
     * 如果在限定时间内线程未执行完毕,则中止(抛出TimeOutException),否则返回结果。
     * @param timeout the maximum time to wait
     * @param unit the time unit of the timeout argument
     * @return the computed result
     * @throws CancellationException if the computation was cancelled
     * @throws ExecutionException if the computation threw an
     * exception
     * @throws InterruptedException if the current thread was interrupted
     * while waiting
     * @throws TimeoutException if the wait timed out
     */
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

使用示例


import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPool {

    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 8, 3000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

    private ThreadPool(){}


    public static ThreadPoolExecutor getInstance() {
        return executor;
    }
}
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.IntStream;

public class Job implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        TimeUnit.SECONDS.sleep(3);
        System.out.println("[" +Thread.currentThread().getName() + "] keep calculation! Current time : " + System.currentTimeMillis());
        AtomicReference<Integer> count = new AtomicReference<>(0);
        IntStream.range(1, 1000).forEach(item -> count.updateAndGet(v -> v + item));
        return count.get();
    }
}
import java.util.concurrent.*;
public class FutureTaskTest {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Job task = new Job();
        ThreadPoolExecutor executor = ThreadPool.getInstance();

        Future<Integer> taskFuture = executor.submit(task);
        executor.shutdown();

        TimeUnit.SECONDS.sleep(2);

        System.out.println("[ " + Thread.currentThread() + " ] keep running!  Current time : " + System.currentTimeMillis());

        System.out.println(taskFuture.get());

        System.out.println("all stop");
    }
}

运行结果

[ Thread[main,5,main] ] keep running!  Current time : 1599973063008
[pool-1-thread-1] keep calculation! Current time : 1599973064009
499500
all stop

主线程的时间是1599973063008,job的时间是1599973064009,两者相差1秒。而程序中,主线程是sleep了2秒,job是sleep了3秒。从这可以说明Future对象是一直在异步执行的。

可以再验证一下:

import java.util.concurrent.*;

public class FutureTaskTest {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
         Job task = new Job();
        ThreadPoolExecutor executor = ThreadPool.getInstance();

        Future<Integer> taskFuture = executor.submit(task);
        executor.shutdown();

//        TimeUnit.SECONDS.sleep(2);

        System.out.println("[ " + Thread.currentThread() + " ] keep running!  Current time : " + System.currentTimeMillis());

        System.out.println(taskFuture.get());

        System.out.println("all stop");
    }
}

运行结果:

[ Thread[main,5,main] ] keep running!  Current time : 1599976879324
[pool-1-thread-1] keep calculation! Current time : 1599976882325
499500
all stop

主线程是1599976879324,job是1599976882325,两者相差了3s,再次验证了上文的结论。

再查看ThreadPoolExecutorcommit()方法时,会看到有涉及到RunnableFuture这个接口。
在这里插入图片描述
跟踪newTaskFor()方法,会发现一个新的类,FutureTask
在这里插入图片描述
FutureTask实现了RunnableFuture接口,并且提供了多个构造方法,如果是Callable,则直接将其赋给callable属性,而如果是Runnable的话,会再涉及到Executors.callable这个方法。
在这里插入图片描述
跟踪Executors.callable方法,会涉及到一个新的RunnableAdapter类。
在这里插入图片描述
在这里插入图片描述
接下来我们分析一下这几个类的关系。
在这里插入图片描述
在这其中,FutureTask利用RunnableAdapterRunnable接口转换为了Callable,达到了一个适配的效果,FutureTask又引用了Callable属性,就此形成了对Callable接口和Runnable接口的支持。

同时,观察ThreadPoolExecutor的所有commit()方法,你会发现,无论是Runnable还是Callable,最终都配转化为FutureTask,这样也达到了一个统一处理的效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值