Java 如何维护将来的任务结果(如果子线程在调用 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();
}
}
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() 调用早得多。这是否意味着 Java 维护可调用的结果,而不管调用者是否调用 get()。 还有人可以解释一下,Java如何实现这样的未来任务。

"所以JIT,不会知道那个开发人员在即将到来的代码中调用了get()。所以我的观点是;每当调用提交(可调用)时,无论是否调用get(),JVM都会保留结果吗?

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

类正在执行这项工作。你递给它一个可调用的,然后它启动并调用。并返回一个令牌类,该类提供结果的未来位置。此类只是返回它拥有的值,或者强制调用 .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

个主线程、7 个获取线程、10 个另一个线程。

我的期望是 get() 可能会返回空值...

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

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

相关内容

最新更新