1.回顾Future和FutureTask

首先,Future是一个接口(Java5就诞生了),而FutureTask是它的实现类。而Future接口则是定义了操作异步任务执行的一些方法,提供了一种异步并行计算的功能,例如:获取异步任务的执行结果、取消异步任务的执行、判断任务是否被取消、判断任务执行是否完毕等。

而异步任务指的是:如果主线程需要执行一个很耗时的计算任务,我们就可以通过Future把这个任务放到异步线程中执行,那么此时主线程继续处理其他任务,异步线程处理完成后,再通过Future获取计算结果。(也即三特点:①多线程;②有返回值;③异步任务)

那么我们为什么需要异步任务呢?我把所有的任务工作都放在main主线程中不行吗? 举个简单的栗子:比如我现在在球场上打球,但是没有小姐姐来观战,我觉得打的没啥意思,但是我要进攻又要抢篮板,没空啊,所以我这个时候可以让场下的替补队员去帮我找点小姐姐来看我打球,这样我继续打球,替补队员去做他的工作,效率自然就提高了啊。(这栗子并不是说替补队员就是干这事的啊,哈哈哈)   在这里,main主线程就是打球,异步线程就是找小姐姐。

1.1 最简单的例子认识Future和FutureTask

package com.szh.demo;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;class MyThread implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println(Thread.currentThread().getName() + " come in call()....");return "hello Callable";}
}public class FutureTaskDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTask<String> futureTask = new FutureTask<>(new MyThread());Thread thread = new Thread(futureTask, "t1");thread.start();System.out.println(futureTask.get());}
}

1.2 Future优点:结合线程池,一定程度上提高程序执行效率

我这里懒省事,就使用了 Executors.newXXX 这种方式。那么熟悉阿里巴巴Java开发规范以及线程池相关原理的,都知道,不建议这样去创建线程池,而是使用ThreadPoolExecutor详细指定线程池的几大参数去创建。

package com.szh.demo;import java.util.concurrent.*;public class FutureTaskDemo2 {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService threadPool = Executors.newFixedThreadPool(3);long startTime = System.currentTimeMillis();FutureTask<String> futureTask1 = new FutureTask<>(() -> {try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}return "task1 over";});threadPool.submit(futureTask1);FutureTask<String> futureTask2 = new FutureTask<>(() -> {try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}return "task2 over";});threadPool.submit(futureTask2);//        System.out.println(futureTask1.get());
//        System.out.println(futureTask2.get());try {TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}long endTime = System.currentTimeMillis();System.out.println("cost time : " + (endTime - startTime) + " ms");System.out.println(Thread.currentThread().getName() + " --- end");threadPool.shutdown();}
}

将代码中的两行 get 注释打开之后,就可以通过Future 获取到异步任务的执行结果了,如下:↓↓↓

这里我们使用Future结合线程池完成了多任务配合,如果我们不使用多线程,将上述代码转换成单线程了话,那么执行时间大概就是 500 + 300 + 300 = 1100 ms左右,对比Future加线程池的 861 ms,还是显著的提高了程序的执行效率。

1.3 Future缺点:get()阻塞 & isDone()轮询

Future虽然可以获取到异步线程的执行结果,就是通过get()方法,但是这个方法会阻塞其他线程的,↓↓↓

package com.szh.demo;import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;/*** public V get() 方法容易阻塞,一般建议放在程序后面,一旦调用就会等到拿到线程执行结果才会离开* 如果不愿意等待太长时间,我希望过期不候,可以自动离开,则使用 public V get(long timeout, TimeUnit unit)* 实际开发中这两个方法都不建议使用*/
public class FutureTaskDemo3 {public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {FutureTask<String> futureTask = new FutureTask<>(() -> {System.out.println(Thread.currentThread().getName() + " come in....");try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}return "task over";});Thread t1 = new Thread(futureTask, "t1");t1.start();System.out.println(Thread.currentThread().getName() + " 忙其他任务了....");System.out.println(futureTask.get());//System.out.println(futureTask.get(3, TimeUnit.SECONDS));
//        while (true) {
//            if (futureTask.isDone()) {
//                System.out.println(futureTask.get());
//                break;
//            } else {
//                try {
//                    TimeUnit.MILLISECONDS.sleep(500);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//                System.out.println("正在处理中,请稍后....");
//            }
//        }}
}

这里程序会首先打印出前两行,而最后一行我们是在main主线程中去获取异步线程的执行结果的,而异步线程计算结果需要5秒,所以这里的main主线程就会被阻塞5秒才可以拿到这个结果。

将代码的 System.out.println(futureTask.get(3, TimeUnit.SECONDS)); 注释打开,它表示我main主线程最多等待你异步线程3秒,3秒之后,无论你异步线程是否将结果计算完毕,我TM都不管你了,我直接暴力结束(过期不候)。↓↓↓

将上面两个get方法注释掉,打开最后的while循环体,这里我们就采用了 isDone() 轮询的方式,但是这种方式的缺点就是:轮询的方式会耗费无谓的CPU资源,而且也不见得能及时得到异步线程的执行结果。↓↓↓↓↓↓

所以综上所述,Future虽然可以获取到异步线程的执行结果,但是它对结果的获取行为都不是很友好(要么阻塞、要么不断轮询耗费CPU资源)。


2.总结

有了上面这几个代码案例,我们对Future、FutureTask结合线程池就有了一定的了解,那么对于简单的业务场景使用这样的方式是完全OK的,但是对于一些较复杂的场景、电商网站的高并发就不行了。

我们可能就需要:

  • 回调通知(应对Future中异步线程的完成时间,完成了你再告诉主线程,不要让主线程一直阻塞等你)。
  • 想将多个异步任务的计算结果组合起来,后一个异步任务的计算结果需要前一个异步任务的执行结果;将两个或多个异步计算合成一个异步计算,这几个异步计算是相互独立的,同时后面这个又依赖前一个处理的结果。
  • 对多个异步线程执行任务的过程中,我们要获取执行任务最快的那个异步线程的计算结果。

3.引出CompletableFuture

上面已经总结了Future的种种缺点,由此就引出了Java8的异步编程利器:CompletableFuture。

我们可以看一下这二者的API数量对比:↓↓↓

这一对比,卧槽,天差地别了。。。就一句话:Future能干的,我 CompletableFuture 照样能干。

关于 CompletableFuture :Java——聊聊JUC中的CompletableFuture

Java——聊聊JUC中的Future和FutureTask相关推荐

  1. Java——聊聊JUC中的CompletableFuture

    文章目录: 1.承接Future和FutureTask 2.CompletableFuture四大静态方法 2.1 runAsync(Runnable runnable) 2.2 runAsync(R ...

  2. Java——聊聊JUC中的Java内存模型(JMM)

    文章目录: 1.CPU缓存模型 2.Java内存模型Java Memory Model 3.JMM规范下的三大特性 3.1 原子性 3.2 可见性 3.3 有序性 4.JMM规范下,多线程对变量的读写 ...

  3. Java——聊聊JUC中的volatile与内存屏障

    文章目录: 1.volatile内存语义 2.内存屏障 2.1 粗分两种:写屏障 2.2 粗分两种:读屏障 2.3 细分四种 3.volatile可见性介绍 4.volatile无原子性介绍 5.vo ...

  4. Akka 系列(五):Java 和 Scala 中的 Future

    随着CPU的核数的增加,异步编程模型在并发领域中的得到了越来越多的应用,由于Scala是一门函数式语言,天然的支持异步编程模型,今天主要来看一下Java和Scala中的Futrue,带你走入异步编程的 ...

  5. Java多线程编程:Callable、Future和FutureTask浅析(多线程编程之四)

    java多线程-概念&创建启动&中断&守护线程&优先级&线程状态(多线程编程之一) java多线程同步以及线程间通信详解&消费者生产者模式&死锁 ...

  6. 【JUC系列】Future异步回调模式

    何为异步回调 前面只是一个例子,对并发的主要模式进行形象的说明. 下面正式来讲下经常使用的几个和并发相关的概念. 1.2.1. 同步.异步.阻塞.非阻塞 一:同步 所谓同步,就是在发出一个功能调用时, ...

  7. Java中的Runnable、Callable、Future、FutureTask

    Java中存在Runnable.Callable.Future.FutureTask这几个与线程相关的类或者接口,在Java中也是比较重要的几个概念,我们通过下面的简单示例来了解一下它们的作用于区别. ...

  8. Java中的Runnable、Callable、Future、FutureTask的区别与示例

    原文地址:http://blog.csdn.net/bboyfeiyu/article/details/24851847 --------------------------------------- ...

  9. java futuretask_java中Future与FutureTask使用与分析

    Future与FutureTask都是用于获取线程执行的返回结果.下面我们就对两者之间的关系与使用进行一个大致的介绍与分析 一.Future与FutureTask介绍: Future位于java.ut ...

最新文章

  1. php什么框架性能高,主流PHP框架性能比较
  2. 《工作DNA》摘录三
  3. pgpool-II3.1 的内存泄漏(四)
  4. shell+ftp+中文乱码_Ftp 命令出现中文乱码问题如何解决,求指教,非常感谢
  5. 11月中30个精心设计的网站案例精选
  6. 两个简单的前台显示构架01
  7. bzoj 1492: [NOI2007]货币兑换Cash
  8. Linux下的less命令
  9. redis decr 防止超卖_一文搞定Redis高级特性与性能调优
  10. 小菜面试 String 篇 之 统计一个字符串中数字,字母,的个数
  11. PPT模板文字环绕效果怎么设置?
  12. vue + element 实现文件上传
  13. 计算机图形学6--讨论多边形
  14. 智源AI日报(2022-08-30): 华为谢凌曦:关于视觉识别领域发展的个人观点
  15. 宝石塔防的贴吧地址:
  16. Git搭建私有服务器
  17. BackTrack 5 notes
  18. 电脑算力测试软件,【分享】linpack ——intel的pc算力测试软件
  19. 如何使用华为云快速搭建个人博客
  20. 日常学习记录——使用Visio2019绘制思维导图

热门文章

  1. 【美团 · 北京沙龙报名】运营效率系统架构演进之道
  2. 机器学习及flinkML算法学习
  3. 专题·深度优先搜索(DFS)【including 2N皇后,等边三角形,中国邮递员问题
  4. phpstom 驼峰插件_推荐四个phpstorm酷炫实用插件 让你写代码的时候不在孤单!
  5. 路由与交换:Cisco交换机配置密码
  6. java强制下线 session,登录的多地挤下线功能 redis session
  7. 通俗易懂数仓建模—Inmon范式建模与Kimball维度建模
  8. 数据仓库(4)基于维度建模的数仓KimBall架构
  9. 101个python小代码 (3)给三个数字排列顺序(从大到小)
  10. 项目数据验证_如何快速发货和验证新项目