Java并发包 Future 由用例引发的思考

先上用例

    static void t1() throws Exception {
        FutureTask task = new FutureTask(() -> "name");
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future future = executorService.submit(task);
        System.out.println(future.get());
        System.out.println(task.get());
        executorService.shutdown();
    }

    static void t2() throws Exception {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future future = executorService.submit(() -> "wang");
        System.out.println(future.get().toString());
        executorService.shutdown();
    }
 
 

再看结果

null
name
wang

思考:
在学习Future接口的时候,对Future的介绍是保存异步计算的结果,并通过get方法阻塞获取异步计算结果。但是在t1方法中future并没有获取到结果,于是我决定一探究竟。最好的方法当然是研究源码,话不多说,直接上源码。

  • 先看ExecutorServicesubmit方法
Future submit(Runnable task);
 Future submit(Callable task);
 Future submit(Runnable task, T result);

该方法有三个重载,分别是传入一个runnable对象;传入一个callable对象;传入一个runnable和泛型对象。这三个方法在AbstractExecutorService类中实现了。

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

    public  Future submit(Callable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

    public  Future submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

可以看到在这三个实现中都只做了两件事:

  • 第一件事调用了newTaskFor方法创建了一个RunnableFuture对象,
  • 第二件事执行execute方法
    首先我们来关注一下newTaskFor这个方法:
    protected  RunnableFuture newTaskFor(Callable callable) {
        return new FutureTask(callable);
    }

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

可以看到这个方法有两个重载,该方法只做了一件事——创建了一个FutureTask对象。
让我们来回顾一下FutureTask的构造函数:

    public FutureTask(Callable callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

在第二个构造函数里面通过调用Executors的静态方法callable,将runnable接口对象包装成了callable接口对象。

    public static  Callable callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter(task, result);
    }

    static final class RunnableAdapter implements Callable {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }

RunnableAdapter类实现了Callable接口,并实现了call方法;在该方法中首先执行了runnable对象,接着返回传入的泛型对象。
接着我们看下execute方法

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

忘记了的话可以参考一下这篇文章

addWorker方法里面执行了线程,其实就是执行了FutureTask对象的run方法。

public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    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方法里面有这一段代码result = c.call();,这段代码执行了callable对象的方法。接下来执行set(result);把结果赋值给outcome属性。

  • 读完上面的代码我们可以解释为什么t1方法的future获取不到值了

由于FutureTask实现了Runnable接口,所以执行的是Future submit(Runnable task)方法。
(1)该方法里面通过调用newTaskFor(task, null)创建了RunnableFuture对象。再来看下该对象的创建过程:

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

    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

    public static  Callable callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter(task, result);
    }

    static final class RunnableAdapter implements Callable {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }

可以看到FutureTaskcallable属性是一个RunnableAdapter对象,该RunnableAdapter对象的task属性就是我们调用executorService.submit(task);传入的FutureTask对象。result属性是调用newTaskFor(task, null)时传入的空值。

注意,此时我们已经有两个FutureTask对象了,一个是JDK帮我们创建对象,该FutureTask对象的callable属性是一个RunnableAdapter对象;一个是我们自己创建的FutureTask task = new FutureTask(() -> "name");对象,该FutureTask对象的callable属性是一个我们创建的lambda表达式。

(2) 该方法里面通过执行了execute方法,execute方法里面传入的参数是一个JDK帮我们创建的FutureTask对象(为了区分,称它为f1)。在execute方法里面执行了addWorker方法;在addWorker方法里面执行了线程,其实就是执行了FutureTask对象的run方法;在run方法里面调用了call方法。由于该callable对象是一个RunnableAdapter对象,所以会执行下面两行代码:

        public T call() {
            task.run();
            return result;
        }

由于该task对象就是我们创建的FutureTask task = new FutureTask(() -> "name");对象(为了区分,称它为f2),所以又会执行FutureTask对象的run方法;在run方法里面调用了call方法。由于这个callable属性是一个我们创建的lambda表达式。所以会直接返回一个字符串,这个字符串会被赋值给f2的outcome属性。当f2的run方法执行完之后,RunnableAdapter对象的call方法会返回result对象,即空值。该空值会被赋值给f1的outcome属性。所以当调用future.get()方法时会获取到空值,当调用task.get()方法时会获取到字符串name

  • 同时我们此时也应该明白为什么t2方法里面能够获取到值了
    由于我们调用的是 Future submit(Callable task)方法。该方法通过newTaskFor(Callable callable)创建了一个RunnableFuture对象,看下创建过程:
    protected  RunnableFuture newTaskFor(Callable callable) {
        return new FutureTask(callable);
    }

    public FutureTask(Callable callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

可以看到该FutureTask对象的callable属性就是我们声明的一个lambda表达式,该表达式返回的字符串最终被赋值给outcome属性,所以但我们调用future.get()方法时会获取到字符串wang

你可能感兴趣的:(Java并发包 Future 由用例引发的思考)