FutureTask的简单使用
今天看了一篇关于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的简单使用相关推荐
- Java多线程类FutureTask源码阅读以及浅析
FutureTask是一个具体的实现类,实现了RunnableFuture接口,RunnableFuture分别继承了Runnable和Future接口,因此FutureTask类既可以被线程执行,又 ...
- java futuretask 源码_java并发编程——FutureTask源码分析
FutureTask的简单示例: FutureTask的应用场景,如果在当前线程中需要执行比较耗时的操作,但又不想阻塞当前线程时,可以把这些作业交给FutureTask,另开一个线程在后台完成,当当前 ...
- java并发编程基础系列(五): 创建线程的四种方式
线程的创建一共有四种方式: 继承于Thread类,重写run()方法: 实现Runable接口,实现里面的run()方法: 使用 FutureTask 实现有返回结果的线程 使用ExecutorSer ...
- 线程锁完成----2021年1月19日19:17:45
线程&锁 || 如何在面试官问到线程时让他拍大腿!!! 这人我要了 线程 线程的相关使用及面试题 线程基础 常见面试题补充 弱智面试题:线程状态有哪些? 线程有哪些终止方式? stop.int ...
- JUC 2020 周阳 尚硅谷 学习笔记
这里写目录标题 一 JUC 介绍 1 进程线程介绍介绍 2 并发并行的介绍 3 wait 和 sleep的区别 4 线程的状态 二 卖票算法的企业级模板实现 企业级简单实现(synchronized) ...
- FutureTask中Treiber堆的实现
2019独角兽企业重金招聘Python工程师标准>>> 在文章FutureTask源码分析中简单说明了FutureTask中使用Treiber堆栈来保存等待结果的线程,本文将详细分析 ...
- 一次搞懂 Runnable、Callable、Future、FutureTask,不懂不要钱!
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 一般创建线程只有两种方式,一种是继承Thread,一种是实 ...
- futuretask使用_JDK源码分析-FutureTask
1. 概述 FutureTask 是一个可取消的.异步执行任务的类,它的继承结构如下: 它实现了 RunnableFuture 接口,而该接口又继承了 Runnable 接口和 Future 接口,因 ...
- Callable、Future和FutureTask
2019独角兽企业重金招聘Python工程师标准>>> 创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任 ...
最新文章
- CHM综述:建立因果关系,合成菌群在植物菌群研究中的机会
- 基于thinkphp的省略图便捷函数
- 论文笔记之: Deep Metric Learning via Lifted Structured Feature Embedding
- tomcat启动报错The JRE could not be found.Edit the server and change the JRE location
- 关于多条id相同,只取其中一条记录的sql语句
- 接口和抽象类的区别。
- 【BZOJ5100】[POI2018]Plan metra 构造
- 【bzoj 1087】[SCOI2005]互不侵犯King(状压dp)
- 怎么让图片平铺_美妆蛋、化妆刷到底应该怎么洗?
- 乒乓球单循环赛_乒乓球循环赛制比赛规则
- 10015---Nginx 常用命令
- 我们通常所说的利率是指_我们通常所说的利率是指()。 A.市场利率B.名义利率C.实际利率D.固定利率...
- 京东百万年薪大佬用JAVA绘制“五子棋棋盘”(附代码)
- 搭建ftp文件服务器
- Dos用户学Unix指南(1)
- 排位赛三B. Loan Repayment
- TJA1044---具有待机模式的高速CAN收发器
- C++ 多态(动态多态)
- VCam 能做什么?
- 如何正确地提出热处理工艺