同时,我们也可以看到 future.get() 方法的返回值为 null。
你说,这不是返回了一个寂寞是干啥?
当你想用标号为 ① 的方法时,我劝你直接用 execute 方式提交任务。还不需要构建一个寂寞的返回值,徒增无用对象。
接下来,我们看看标号为 ② 的方法是怎么用的:
public class JDKThreadPoolExecutorTest { public static void main(String[] args) throws Exception { ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10)); AtomicInteger atomicInteger = new AtomicInteger(); Future<AtomicInteger> future = executor.submit(() -> { System.out.println("关注why技术"); //在这里进行计算逻辑 atomicInteger.set(5201314); }, atomicInteger); System.out.println("future的内容:" + future.get()); Thread.currentThread().join(); } }
可以看到改造之后,确实是调用了标号为 ② 的方法:
future.get() 方法的输出值也是异步任务中我们经过计算后得出的 5201314。
你看,渣男就是这样,明明不懂你,还非得用甜言蜜语来轰炸你。呸。
好了。综上,线程池的提交方式一共有四种:一种 execute,无返回值。三种 submit,有返回值。
submit 中按照提交任务的类型又分为两种:一个是 Callable,一个是 Runable。
submit 中 Runable 的任务类型又有两个重载方法:一个返回了个寂寞,一个返回了个渣男。哦,不。一个返回了个寂寞,一个返回了个对象。
这个时候就有人要站出来说:你说的不对,你就是瞎说,明明就只有 execute 这一种提交方式。
是的,“只有 execute 这一种提交方式”这一种说法也是没错的。
请看源码:
写到这里我不禁想起了我的第三篇文章,真是奇怪的时间线开始收缩了的感觉,《有的线程它死了,于是它变成一道面试题》,这篇文章里面聊到了不同提交方式,对于异常的不同处理方式。
我就问你:一个线程池中的线程异常了,那么线程池会怎么处理这个线程?
你要是不知道,可以去看看这篇文章,毕竟,有可能在面试的时候遇到的:
好,上面这些东西捋清楚了之后。我们再聚焦到返回值 Future 上:
从上面的代码我们可以看出,当我们想要返回值的时候,都需要调用下面的这个 get() 方法:
总之就是有可能要等的。只要等,那么就是阻塞。只要是阻塞,就是一个假异步。
所以总结一下这种场景下返回的 Future 的不足之处:
- 只有主动调用 get 方法去获取值,但是有可能值还没准备好,就阻塞等待。
- 任务处理过程中出现异常会把异常隐藏,封装到 Future 里面去,只有调用 get 方法的时候才知道异常了。
写到这里的时候我不禁想起一个形象的例子,我给你举一个。
假设你想约你的女神一起去吃饭。女神嘛,肯定是要先画个美美的妆才会出去逛街的。而女神化妆就可以类比为我们提交的一个异步任务。
假设你是一个小屌丝,那么女神就会对你说:我已经开始化妆了,你到楼下了就给我打电话。
然后你就收拾行头准备出发,这就是你提交异步任务后还可以做一些自己的事情。
你花了一小时到了女神楼下,打电话给她:女神你好,我到你楼下了。
女神说:你先等着吧,我的妆还没画好呢。
于是你开始等待,无尽的等待。这就是不带超时时间的 future.get() 方法。