3

在调用子可调用线程的 get 方法之前,我在程序下面运行,主线程首先完成。

public class Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {        
        ExecutorService exec =  Executors.newFixedThreadPool(1);
        Future<String> f =  exec.submit(new TThread());
        Thread.sleep(10000);
        System.out.println("Main Thread "+System.currentTimeMillis());      
        String s  = (String) f.get();
        System.out.println(s);
        exec.shutdown();
    }
}

class TThread implements Callable<String>{
    @Override
    public String call() throws Exception{
        Thread.sleep(3000);
        System.out.println("TThread Thread "+System.currentTimeMillis());
        return "something";
    }
}

输出是

TThread Thread 1497248667516
Main Thread 1497248674515
something

我的期望是 get() 可能返回 null,因为 TThread 线程比 get() 调用更早完成。这是否意味着无论调用者是否调用 get(),Java 都会维护 callable 的结果。也有人可以解释一下,Java如何实现这样的未来任务。

4

2 回答 2

1

“所以 JIT,不会知道开发人员在即将到来的代码中是否调用了 get()。所以我的观点是;每当调用 submit(callable) 时,JVM 是否会保留结果,而不管 get() 是否被调用或不是?”

在我看来,您在这里的理解是一个大问题。首先,JIT 和 JVM 都不关心这样的高级命令。它们是实现它的 ExecutorService 类的结果。当您使用特定结果提供线程调用它时,它会返回一个称为未来任务的对象。任务本身只是一个空壳,如果它有值则基本上返回该值,如果没有则调用 wait()。这会导致线程停止操作。然后 ExecutorService 调用的另一个线程返回它的值。当它返回值时,它会将该值传递给 FutureTask,从而调用 notify() 来启动线程。

ExecutorService 类正在做这项工作。你给它传递了一个 Callable,然后它启动并调用它。并返回一个提供结果未来位置的令牌类。此类仅返回其拥有的值,或强制调用 .get() 的威胁停止操作,直到它可以返回该值。

您可以自己实现相同的逻辑。JVM 关心的是 wait() 和 notify() 命令本身的线程内容,而不是像花哨的包装类这样的更高级别的东西,以设置范例在这里的工作方式。还有许多其他类似的东西,例如 BlockingQueues 和 AsyncTasks 以及一堆其他帮助程序类,它们执行并行运行的内部逻辑。Get 的逻辑基本上是这样说的:如果我有值,则返回值。如果我不等。然后返回值。诀窍是您在提交期间启动的另一个线程有代码说将值放入该类并通知()任何停止的线程等待该对象。

JVM 不处理这个类,只是以或多或少的字节码执行命令,JIT 在运行时对其进行更多编译。但是,它没有运行 java 源代码,到那时它还不知道这个类是什么。但是,它确实知道如何让线程等待直到有一个值,因为这就是 Executor 代码实际上所做的,它只是为您做了一些有用的工作,因为自己处理阻塞是一项艰巨的任务。

它实际上并没有预测未来。这是放置未来结果的地方。如果你提前调用它,你的线程会暂停,直到结果可用。


您对错误的事物进行基准测试。但不是。.get() 将“在必要时等待计算完成,然后检索其结果。” 所以。会发生什么是意料之中的。只是将结果保留在未来的任务中。无论您在主线程中休眠多长时间,您的设置都将始终为您提供结果。只是你推迟调用 .get() 直到最后一刻,这样你就可以并行运行这些操作。

public class Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {        
        ExecutorService exec =  Executors.newFixedThreadPool(1);
        Future<String> f =  exec.submit(new TThread());
        Thread.sleep(10000);
        System.out.println("Main Thread "+System.currentTimeMillis());      
        String s  = (String) f.get();
        System.out.println(s);
        exec.shutdown();
    }
}

您提交了主题。睡了10秒。然后报了时间。线程开始后 10 秒。然后,您调用 .get() ,它会在需要时等待,但 TThread 已经完成,因此它返回了答案。

class TThread implements Callable<String>{
    @Override
    public String call() throws Exception{
        Thread.sleep(3000);
        System.out.println("TThread Thread "+System.currentTimeMillis());
        return "something";
    }
}

你睡了 3 秒,在程序开始执行后 3 秒输出时间。并返回了一些东西。

这正是你的结果。关键是如果你需要你调用 get 的线程的答案,它会得到答案。但是,如果你可以做一段时间的其他事情,然后调用 get,你会在这段时间内并行运行,无论你有没有答案,你都会得到结果。但是,如果它还没有完成,它会导致当前线程阻塞。在这种情况下,结果有答案,只是将其返回给您。但是,如果你翻转时代。你仍然会得到同样的东西。主线程会报 3 秒,TThread 会报 10 秒。你需要做的是报出 get 命令后的时间。在这种情况下,您将看到 10 个用于主要,3 个用于其他。但是,当您切换时间时,主线程将获得 3 个,get 获得 7 个,其他线程获得 10 个。

于 2017-06-12T07:05:57.583 回答
0

我的期望是 get() 可能返回 null ......

不,get 将仅返回类中call方法返回的可能值TThread,在您的情况下为“Something”,剩下的选项是调用方法被中断,然后根本没有返回,因为异常在堆栈中传播到
executor.submit()调用。

您在 Java 中的微基准无法正常工作......

于 2017-06-12T07:00:19.473 回答