多线程基础(六)并行程序设计模式之——Future

1. 问题
一个用户请求需要请求两个接口,每个接口耗时1s,那么整体的响应时间应该是2s,如果我们想让请求在1s就返回给用户怎么做呢?
通过我们之前几篇文章的学习,实现这个功能很简单,只需要开启一个新线程处理一个接口,主线程处理一个接口,两个接口都返回后钻皇数据返回给用户就可以了。
由于Thread或Runnable并不能返回结果,你可能会这样写代码

        final String[] res1 = {null};
        final String[] res2 = {null};
        new Thread() {
            @Override
            public void run() {
                String result = "远程调用耗时的方法";
                res1[0] = result;
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                String result = "远程调用耗时的方法";
                res2[0] = result;
            }
        }.start();
        while (res1[0] != null && res2[0] != null) {
            //.....组装数据并返回
        }

虽然能解决问题但是这个写法也太。。。。。。
幸好jdk给我们封装了一种Future模式

2. Future
Callable接口,与Runable接口相比它可以返回数据

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

Future接口

public interface Future<V> {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
}

3. Callable案例演示

public class ThreadPoolTest {
    public static void main(String[] args) throws Exception {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1,
                1L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                new ThreadPoolExecutor.DiscardPolicy());
        long start = System.currentTimeMillis();
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call() throws InterruptedException {
                Thread.sleep(1000L);
                return "远程调用耗时的方法";
            }
        };
        Future<?> future = executor.submit(callable);
        Thread.sleep(1000L);
        Object s = future.get();
        System.out.println(s);
        long end = System.currentTimeMillis();
        System.out.println("共耗时:" + (end - start) + "ms");
        executor.shutdown();
    }
}

执行结果:

远程调用耗时的方法
共耗时:1034ms

4. FutureTask案例演示

public class ThreadPoolTest {
    public static void main(String[] args) throws Exception {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3,
                1L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                new ThreadPoolExecutor.DiscardPolicy());
        long start = System.currentTimeMillis();

        FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(1000L);
                return "远程调用耗时的方法";
            }
        });
        
        executor.submit(futureTask);
        Thread.sleep(1000L);
        Object s = futureTask.get();
        System.out.println(s);
        long end = System.currentTimeMillis();
        System.out.println("共耗时:" + (end - start) + "ms");
        executor.shutdown();
    }
}

执行结果:

远程调用耗时的方法
共耗时:1034ms

5. jdk-Future模式原理
我们从入口开始看,看一下线程池submit方法:

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

任务提交后,任务被封装成一个RunnableFuture对象

    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

使用的是FutureTask类,此类实现了Runnable和Future接口

public class FutureTask<V> implements RunnableFuture<V> 

FutureTask类调用get方法时会阻塞直到拿到结果:

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

下面看一下awaitDone()方法
在这里插入图片描述
这里会循环判断state,state是一个volatile状态变量,当线程执行完毕会改变他的状态为完成状态,否则get方法会一直阻塞

    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;

其实原理和我们第一步写的代码是类似的,他只不过把循环判断状态的事情封装了一下,定义了一个可以返回结果的Future接口类来做这个事情。

发布了52 篇原创文章 · 获赞 7 · 访问量 3816

猜你喜欢

转载自blog.csdn.net/maomaoqiukqq/article/details/99698220