今天看了一篇关于FutureTask的文章,觉得写得乱七八糟的。刚开始还觉得有点意思,后来越看越不对劲,看了评论一位老哥说的才明白到底出错在哪里了。我简单写一下他的逻辑,他的场景逻辑是这样的:

需求需求场景:等早餐过程中,包子需要3秒,凉菜需要1秒,普通的多线程需要四秒才能完成。先等凉菜,再等包子,因为等凉菜时,普通多线程启动start()方法,执行run()中具体方法时,没有返回结果,所以如果要等有返回结果,必须是要1秒结束后才知道结果。

然后代码实现按我的Java风格是这样实现的:

public class Test {public static void main(String[] args) throws InterruptedException {long startTime = System.currentTimeMillis();Thread t1 = new Thread1();Thread t2 = new Thread2();t1.start();t1.join();t2.start();t2.join();long endTime = System.currentTimeMillis();System.out.println("共使用时间为:"+ (endTime - startTime));}private static class Thread1 extends Thread{@Overridepublic void run() {try{Thread.sleep(3000L);System.out.println("包子准备完毕");}catch (InterruptedException e){e.printStackTrace();}}}private static class Thread2 extends Thread{@Overridepublic void run() {try{Thread.sleep(1000L);System.out.println("凉菜准备完毕");}catch (InterruptedException e){e.printStackTrace();}}}
}

最后这样算出来,时间上消耗是4秒左右,除去虚拟机的操作,就是4秒没错。

然后据他的文章,把代码采用FutureTask改成这个样子,使用时间是在3秒

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class Test2 {private static Callable<String> callable1 = () -> {try{Thread.sleep(3000L);}catch (InterruptedException e){e.printStackTrace();}return "包子准备完毕";};private static Callable<String> callable2 = () -> {try{Thread.sleep(1000L);}catch (InterruptedException e){e.printStackTrace();}return "凉菜准备完毕";};private static FutureTask<String> ft1 = new FutureTask<>(callable1);private static FutureTask<String> ft2 = new FutureTask<>(callable2);public static void main(String[] args) throws ExecutionException, InterruptedException {long startTime = System.currentTimeMillis();new Thread(ft1).start();new Thread(ft2).start();System.out.println(ft1.get());System.out.println(ft2.get());long endTime = System.currentTimeMillis();System.out.println("共使用时间为:"+ (endTime - startTime));}
}

当然啦,这样算出来的时间的确是3秒。但是他这里玩了一个移花接木的小技巧,他在Test1里面的psvm中对于t1和t2的start和join操作是先t1.start()再t1.join()阻塞主线程,然后再t2.start()以及t2.join()阻塞主线程,这样算下来当然是4秒了。然而你再futuretask的用例里面是先对ft1和ft2这两个FutureTask传入Thread中start再使用get方法分别阻塞主线程,这样就会是3秒了。

如果你在Test1里面修改为先对t1和t2进行start操作,再对t1和t2进行join操作,那么Test1使用的时间也是3秒。

事实上,FutureTask的作用也不是处理这种问题的。FutureTask处理的应该是各线程之间结果依赖的问题,比如下面的这个问题:

需求场景:需要泡茶,现在有一壶冷水和一些茶叶。烧开水的时间为3秒,泡茶动作时间为1秒,不能使用冷水泡茶,必须在烧完开水之后才能泡茶。

对于这种问题,FutureTask的作用才能发挥出来。现在我们假设烧开水为Thread1,泡茶动作为Thread2,那么很显然这个解决步骤是先执行Thread1,然后等水由冷水变成热水,再泡茶。那么实现代码如下:

public class Test4 {private static volatile boolean hot_water = false;private static class HotWaterThread extends Thread{@Overridepublic void run() {try{Thread.sleep(3000L);hot_water = true;System.out.println("水已经烧开");}catch (InterruptedException e){e.printStackTrace();}}}private static class TeaThread extends Thread{@Overridepublic void run() {try{Thread t1 = new HotWaterThread();t1.start();t1.join();if(hot_water){Thread.sleep(1000L);System.out.println("茶泡好了");}else{System.out.println("没有热水");}}catch (InterruptedException e){e.printStackTrace();}}}public static void main(String[] args) throws InterruptedException {long start = System.currentTimeMillis();Thread t2 = new TeaThread();t2.start();t2.join();long end = System.currentTimeMillis();System.out.println("all time:" + (end-start));}
}

这里需要使用一个hot_water的布尔全局变量作为标识符表示线程1已经执行完毕,再来执行线程2。如果我们这个时候引入FutureTask这个类,那么代码则可以改写成这个样子:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class Test3 {private static final Callable<String> callable1 = () -> {Thread.sleep(3000L); // 模拟烧水动作System.out.println("烧开水成功");return "hot_water";};private static final FutureTask<String> ft1 = new FutureTask<>(callable1);private static final Callable<String> callable2 = () -> {new Thread(ft1).start();if(ft1.get().equals("hot_water")){Thread.sleep(1000L); // 模拟泡茶动作System.out.println("泡茶成功");return "tea is done";}else{return "tea is not done";}};private static final FutureTask<String> ft2 = new FutureTask<>(callable2);public static void main(String[] args) throws InterruptedException, ExecutionException {long startTime = System.currentTimeMillis();new Thread(ft2).start();System.out.println(ft2.get());long endTime = System.currentTimeMillis();System.out.println("共使用时间为:"+ (endTime - startTime));}
}

我这里是全自动烧水了,其实现效果是差不多的,所不同的是在FutureTask的加持下,我是不需要再手动设置一个全局变量作为水是否烧开的标识符。相当于我在偷了一个懒把手动设置变量的活交给了FutureTask帮我干,而且这样也能保证我的代码出错的可能性变小。

所以说什么FutureTask让线程执行时间能够变短,那都是扯淡。都是一些上层API库,你算法逻辑再强也不能违背物理规率,包括Kotlin中的协程,也只是对Java线程池的重新封装而已,方便了各个线程的切换,并没有实际性突破线程的物理规律。

现在很多的创新都只是一个,有你,我用得更方便bug会变少,没你,我一样也能写代码的状态。所以,还是客观看待问题。

FutureTask的简单使用相关推荐

  1. Java多线程类FutureTask源码阅读以及浅析

    FutureTask是一个具体的实现类,实现了RunnableFuture接口,RunnableFuture分别继承了Runnable和Future接口,因此FutureTask类既可以被线程执行,又 ...

  2. java futuretask 源码_java并发编程——FutureTask源码分析

    FutureTask的简单示例: FutureTask的应用场景,如果在当前线程中需要执行比较耗时的操作,但又不想阻塞当前线程时,可以把这些作业交给FutureTask,另开一个线程在后台完成,当当前 ...

  3. java并发编程基础系列(五): 创建线程的四种方式

    线程的创建一共有四种方式: 继承于Thread类,重写run()方法: 实现Runable接口,实现里面的run()方法: 使用 FutureTask 实现有返回结果的线程 使用ExecutorSer ...

  4. 线程锁完成----2021年1月19日19:17:45

    线程&锁 || 如何在面试官问到线程时让他拍大腿!!! 这人我要了 线程 线程的相关使用及面试题 线程基础 常见面试题补充 弱智面试题:线程状态有哪些? 线程有哪些终止方式? stop.int ...

  5. JUC 2020 周阳 尚硅谷 学习笔记

    这里写目录标题 一 JUC 介绍 1 进程线程介绍介绍 2 并发并行的介绍 3 wait 和 sleep的区别 4 线程的状态 二 卖票算法的企业级模板实现 企业级简单实现(synchronized) ...

  6. FutureTask中Treiber堆的实现

    2019独角兽企业重金招聘Python工程师标准>>> 在文章FutureTask源码分析中简单说明了FutureTask中使用Treiber堆栈来保存等待结果的线程,本文将详细分析 ...

  7. 一次搞懂 Runnable、Callable、Future、FutureTask,不懂不要钱!

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 一般创建线程只有两种方式,一种是继承Thread,一种是实 ...

  8. futuretask使用_JDK源码分析-FutureTask

    1. 概述 FutureTask 是一个可取消的.异步执行任务的类,它的继承结构如下: 它实现了 RunnableFuture 接口,而该接口又继承了 Runnable 接口和 Future 接口,因 ...

  9. Callable、Future和FutureTask

    2019独角兽企业重金招聘Python工程师标准>>> 创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任 ...

最新文章

  1. CHM综述:建立因果关系,合成菌群在植物菌群研究中的机会
  2. 基于thinkphp的省略图便捷函数
  3. 论文笔记之: Deep Metric Learning via Lifted Structured Feature Embedding
  4. tomcat启动报错The JRE could not be found.Edit the server and change the JRE location
  5. 关于多条id相同,只取其中一条记录的sql语句
  6. 接口和抽象类的区别。
  7. 【BZOJ5100】[POI2018]Plan metra 构造
  8. 【bzoj 1087】[SCOI2005]互不侵犯King(状压dp)
  9. 怎么让图片平铺_美妆蛋、化妆刷到底应该怎么洗?
  10. 乒乓球单循环赛_乒乓球循环赛制比赛规则
  11. 10015---Nginx 常用命令
  12. 我们通常所说的利率是指_我们通常所说的利率是指()。 A.市场利率B.名义利率C.实际利率D.固定利率...
  13. 京东百万年薪大佬用JAVA绘制“五子棋棋盘”(附代码)
  14. 搭建ftp文件服务器
  15. Dos用户学Unix指南(1)
  16. 排位赛三B. Loan Repayment
  17. TJA1044---具有待机模式的高速CAN收发器
  18. C++ 多态(动态多态)
  19. VCam 能做什么?
  20. 如何正确地提出热处理工艺

热门文章

  1. STL源码剖析---移动advance
  2. Python-Django毕业设计基于的汉服服装租赁系统(程序+Lw)
  3. 工作中已经没有主人翁态度的我
  4. 黑猴子的家:Python 嵌套for循环
  5. 前端开发HTML-图片、音频等媒体标签,超链接标签
  6. MySQL详解(四)——高级 2.0
  7. 数字城市福清防汛应急平台(泉舟时代)
  8. 北境之地服务器没有响应,《北境之地》常见问题解决方法
  9. 数据增强(图像)初探
  10. SqlServer教程四:SqlSugar的查询