Java异步编程Future应用

简介: Java异步编程Future应用

1 Future接口介绍

此时有的人会说,对于任务并行需求,直接通过多线程实现不就可以了, 要注意,对于多线程的实现,java提供了三种方式:继承Thread类、实现Runnable接口和实现Callable接口。

但是业务代码在执行时会考虑执行顺序的问题,直接基于这些方式实现多线程会出现两个问题:

1)要想控制线程执行顺序,会通过join()等待线程结束,那这样的话又回归到了阻塞式调用的思路上,违背了并行的需求。 另外还可以通过wait()、notify()、notifyAll()结合状态变量实现,但实现起来过于复杂。

2)线程执行完之后,要想获取线程执行结果,还要用过共享变量或线程间通信等方式来获取,同样过于复杂。为了解决上述问题,Java5中推出了Future,其初衷就是用于构建复杂并行操作。内部方法在返回时,不是返回一个值,而是返回Future对象。其本质是在执行主业务的同时,异步的执行其他分业务,从而利用原本需要同步执行时的等待时间去执行其他的业务,当需要获取其结果时,再进行获取。


Java官网对于Future的描述:

3eee35a9b0cc41118420aa9aba7c053c.png

Future表示异步计算的结果。 提供了一些方法来检查计算是否完成,等待其完成以及检索计算结果。 只有在计算完成后才可以使用get方法检索结果,必要时将其阻塞,直到准备就绪为止。 取消通过cancel方法执行。 提供了其他方法来确定任务是正常完成还是被取消。 一旦计算完成,就不能取消计算。

49b1cdb2e52f4de988c705617f0b28d0.png

在Future接口中有五个抽象方法:

d9ff84a724114928a3b3163d4d48c445.png

cancel():取消任务, 取消成功返回true;入参mayInterruptIfRunning表示是否允许取消正在执行中的任务。


cd3e2817293b4d2f93459c99080c72d3.png

isCancelled():返回布尔值,代表是否取消成功。

ffa7d8ae5b7d4a8189816874b01a2465.png


isDone():返回布尔值,代表是否执行完毕。

27f45dfb2d01449b9e20d2d9bd43baf6.png


get():返回Future对象,获取执行结果,如果任务没有完成会阻塞到任务完成再返回。


2 Future应用

Future的使用通常需要配合ExecutorService和Callable一起

使用,使用示例如下:

public class FutureAsyncDemo {
  static Random random = new Random();
  static ExecutorService executor =
Executors.newCachedThreadPool();
  //接收文章名称,获取并计算文章分数
  public static int getArticleScore(String
aname){
    Future<Integer> futureA =
executor.submit(new
CalculateArticleScoreA());
    Future<Integer> futureB =
executor.submit(new
CalculateArticleScoreA());
    Future<Integer> futureC =
executor.submit(new
CalculateArticleScoreA());
    doSomeThingElse();
    Integer a = null;
    try {
      a = futureA.get();
   } catch (InterruptedException e) {
      futureA.cancel(true);
      e.printStackTrace();
   } catch (ExecutionException e) {
      futureA.cancel(true);
       e.printStackTrace();
   }
    Integer b = null;
    try {
      b = futureB.get();
   } catch (InterruptedException e) {
      futureB.cancel(true);
      e.printStackTrace();
   } catch (ExecutionException e) {
      futureB.cancel(true);
      e.printStackTrace();
   }
    Integer c = null;
    try {
      c = futureC.get();
   } catch (InterruptedException e) {
      futureC.cancel(true);
      e.printStackTrace();
   } catch (ExecutionException e) {
      futureC.cancel(true);
      e.printStackTrace();
   }
    executor.shutdown();
    return a+b+c;
 }
  private static void doSomeThingElse() {
    System.out.println("exec other
things");
 }
  public static void main(String[] args) {
 System.out.println(getArticleScore("demo"))
;
 }
}
class CalculateArticleScoreA implements
Callable<Integer>{
  @Override
  public Integer call() throws Exception {
    //业务代码
    Random random = new Random();
    TimeUnit.SECONDS.sleep(3);
 System.out.println(Thread.currentThread().g
etName());
    return random.nextInt(100);
 }
}

执行结果

exec other things
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
159

上述方法改造了calculateArticleScore(),在其内部基于线程池调用重写了Callable接口中的call(),并在call()中对具体业

务完成编码,并且让其在执行时睡三秒钟。根据结果可以看到,先调用了计算文章分数方法,其内部开启了子线程去执行任务,并且子线程在执行时,并没有阻塞主线程的执行。当主线程需要结果时,在通过返回的Future来获取子任务中的返回值。


3 Future并行变串行问题解析

刚才已经基于Future演示了并行执行的效果,已经达到了期望,但是在使用的过程中,其实还有个坑需要说明。对于

Future的使用,如稍加不注意,就会让并行变为串行。

示例代码如下:

public class FutureAsyncDemo {
  static ExecutorService executor =
Executors.newCachedThreadPool();
  //接收文章名称,获取并计算文章分数
  public static int getArticleScore(String
aname){
    Future<Integer> futureA =
executor.submit(new
CalculateArticleScoreA());
    Future<Integer> futureB =
executor.submit(new
CalculateArticleScoreB());
     Future<Integer> futureC =
executor.submit(new
CalculateArticleScoreC());
    doSomeThingElse();
    Integer a = 0;
    try {
      a = futureA.get();
   } catch (InterruptedException e) {
      futureA.cancel(true);
      e.printStackTrace();
   } catch (ExecutionException e) {
      futureA.cancel(true);
      e.printStackTrace();
   }
    Integer b = 0;
    try {
      b = futureB.get();
   } catch (InterruptedException e) {
      futureB.cancel(true);
      e.printStackTrace();
   } catch (ExecutionException e) {
      futureB.cancel(true);
      e.printStackTrace();
   }
    Integer c = 0;
    try {
      c = futureC.get();
   } catch (InterruptedException e) {
       futureC.cancel(true);
      e.printStackTrace();
   } catch (ExecutionException e) {
      futureC.cancel(true);
      e.printStackTrace();
   }
    executor.shutdown();
    return a+b+c;
 }
  private static void doSomeThingElse() {
    System.out.println("exec other
things");
 }
  public static void main(String[] args) {
 System.out.println(getArticleScore("demo"))
;
 }
}
class CalculateArticleScoreA implements
Callable<Integer>{
  @Override
  public Integer call() throws Exception {
    Random random = new Random();
    TimeUnit.SECONDS.sleep(10);
 System.out.println(Thread.currentThread().g
etName());
    return random.nextInt(100);
 }
}
class CalculateArticleScoreB implements
Callable<Integer>{
  @Override
  public Integer call() throws Exception {
    Random random = new Random();
    TimeUnit.SECONDS.sleep(20);
 System.out.println(Thread.currentThread().g
etName());
    return random.nextInt(100);
 }
}
class CalculateArticleScoreC implements
Callable<Integer>{
  @Override
  public Integer call() throws Exception {
    Random random = new Random();
    TimeUnit.SECONDS.sleep(30);
 System.out.println(Thread.currentThread().g
etName());
    return random.nextInt(100);
 }
 }

上述代码加计算得分方法复制出来两份,各自休眠10秒、20秒、30秒。当方法返回Future之后,调用get()进行值获取时,发现每次调用时都需要进行等待。这样可以发现,之前的并行现在变成了串行了!!!! 这个问题为什么会产生呢?需要看一下Future中对于get()的介绍

d7a99d350ff94ae59c14a582f3866508.png

根据源码可知,当调用get()时,其会等待对应方法执行完毕后,才会返回结果,否则会一直等待。因为这个设定,所以上述代码则出现并行变串行的效果。

对于这个问题的解决,可以调用get()的重载,get(longtimeout, TimeUnit unit)。设置等待的时长,如果超时则抛出TimeoutException。


使用示例如下:

public class FutureAsyncDemo {
  static Random random = new Random();
  static ExecutorService executor =
Executors.newCachedThreadPool();
  //接收文章名称,获取并计算文章分数
   public static int
getArticleScore(String aname){
    Future<Integer> futureA =
executor.submit(new
CalculateArticleScoreA());
    Future<Integer> futureB =
executor.submit(new
CalculateArticleScoreB());
    Future<Integer> futureC =
executor.submit(new
CalculateArticleScoreC());
    doSomeThingElse();
    Integer a = 0;
    try {
      a = futureA.get();
   } catch (InterruptedException e) {
      futureA.cancel(true);
      e.printStackTrace();
   } catch (ExecutionException e) {
      futureA.cancel(true);
      e.printStackTrace();
   }
    Integer b = 0;
    try {
       b = futureB.get(3L,
TimeUnit.SECONDS);
   } catch (TimeoutException e) {
      e.printStackTrace();
   }
    catch (InterruptedException e) {
      futureB.cancel(true);
      e.printStackTrace();
   } catch (ExecutionException e) {
      futureB.cancel(true);
      e.printStackTrace();
   }
    Integer c = 0;
    try {
      c = futureC.get();
   } catch (InterruptedException e) {
      futureC.cancel(true);
      e.printStackTrace();
   } catch (ExecutionException e) {
      futureC.cancel(true);
      e.printStackTrace();
   }
    executor.shutdown();
    return a+b+c;
 }
  private static void doSomeThingElse() {
     System.out.println("exec other
things");
 }
  public static void main(String[] args)
{
 System.out.println(getArticleScore("demo")
);
 }
}
class CalculateArticleScoreA implements
Callable<Integer>{
  @Override
  public Integer call() throws Exception
{
    Random random = new Random();
    TimeUnit.SECONDS.sleep(10);
 System.out.println(Thread.currentThread().
getName());
    return random.nextInt(100);
 }
}
class CalculateArticleScoreB implements
Callable<Integer>{
  @Override
  public Integer call() throws Exception
{
    Random random = new Random();
    TimeUnit.SECONDS.sleep(20);
 System.out.println(Thread.currentThread().
getName());
    return random.nextInt(100);
 }
}
class CalculateArticleScoreC implements
Callable<Integer>{
  @Override
  public Integer call() throws Exception
{
    Random random = new Random();
    TimeUnit.SECONDS.sleep(30);
 System.out.println(Thread.currentThread().
getName());
    return random.nextInt(100);
 }
}

在上述方法中,对于B的get()设置了超时时间三秒钟,如果当调用其获取返回值时,如果超过三秒仍然没有返回结果,则抛出超时异常,接着方法会再次向下运行。


对于Future来说,它能够支持任务并发执行,对于任务结果的获取顺序是按照提交的顺序获取,在使用的过程中建议通过CPU高速轮询的方式获取任务结果,但这种方式比较耗费资源。不建议使用

目录
相关文章
|
13天前
|
人工智能 Java API
Java与大模型集成实战:构建智能Java应用的新范式
随着大型语言模型(LLM)的API化,将其强大的自然语言处理能力集成到现有Java应用中已成为提升应用智能水平的关键路径。本文旨在为Java开发者提供一份实用的集成指南。我们将深入探讨如何使用Spring Boot 3框架,通过HTTP客户端与OpenAI GPT(或兼容API)进行高效、安全的交互。内容涵盖项目依赖配置、异步非阻塞的API调用、请求与响应的结构化处理、异常管理以及一些面向生产环境的最佳实践,并附带完整的代码示例,助您快速将AI能力融入Java生态。
142 12
|
20天前
|
安全 Java API
Java SE 与 Java EE 区别解析及应用场景对比
在Java编程世界中,Java SE(Java Standard Edition)和Java EE(Java Enterprise Edition)是两个重要的平台版本,它们各自有着独特的定位和应用场景。理解它们之间的差异,对于开发者选择合适的技术栈进行项目开发至关重要。
100 1
|
2月前
|
设计模式 XML 安全
Java枚举(Enum)与设计模式应用
Java枚举不仅是类型安全的常量,还具备面向对象能力,可添加属性与方法,实现接口。通过枚举能优雅实现单例、策略、状态等设计模式,具备线程安全、序列化安全等特性,是编写高效、安全代码的利器。
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
Java 大视界 -- Java 大数据机器学习模型在自然语言生成中的可控性研究与应用(229)
本文深入探讨Java大数据与机器学习在自然语言生成(NLG)中的可控性研究,分析当前生成模型面临的“失控”挑战,如数据噪声、标注偏差及黑盒模型信任问题,提出Java技术在数据清洗、异构框架融合与生态工具链中的关键作用。通过条件注入、强化学习与模型融合等策略,实现文本生成的精准控制,并结合网易新闻与蚂蚁集团的实战案例,展示Java在提升生成效率与合规性方面的卓越能力,为金融、法律等强监管领域提供技术参考。
|
2月前
|
存储 监控 数据可视化
Java 大视界 -- 基于 Java 的大数据可视化在企业生产运营监控与决策支持中的应用(228)
本文探讨了基于 Java 的大数据可视化技术在企业生产运营监控与决策支持中的关键应用。面对数据爆炸、信息孤岛和实时性不足等挑战,Java 通过高效数据采集、清洗与可视化引擎,助力企业构建实时监控与智能决策系统,显著提升运营效率与竞争力。
|
2月前
|
存储 人工智能 算法
Java 大视界 -- Java 大数据在智能医疗影像数据压缩与传输优化中的技术应用(227)
本文探讨 Java 大数据在智能医疗影像压缩与传输中的关键技术应用,分析其如何解决医疗影像数据存储、传输与压缩三大难题,并结合实际案例展示技术落地效果。
|
2月前
|
存储 数据采集 搜索推荐
Java 大视界 -- Java 大数据在智慧文旅旅游景区游客情感分析与服务改进中的应用实践(226)
本篇文章探讨了 Java 大数据在智慧文旅景区中的创新应用,重点分析了如何通过数据采集、情感分析与可视化等技术,挖掘游客情感需求,进而优化景区服务。文章结合实际案例,展示了 Java 在数据处理与智能推荐等方面的强大能力,为文旅行业的智慧化升级提供了可行路径。
Java 大视界 -- Java 大数据在智慧文旅旅游景区游客情感分析与服务改进中的应用实践(226)
|
2月前
|
机器学习/深度学习 安全 Java
Java 大视界 -- Java 大数据在智能金融反洗钱监测与交易异常分析中的应用(224)
本文探讨 Java 大数据在智能金融反洗钱监测与交易异常分析中的应用,介绍其在数据处理、机器学习建模、实战案例及安全隐私等方面的技术方案与挑战,展现 Java 在金融风控中的强大能力。
|
2月前
|
机器学习/深度学习 算法 Java
Java 大视界 -- Java 大数据机器学习模型在生物信息学基因功能预测中的优化与应用(223)
本文探讨了Java大数据与机器学习模型在生物信息学中基因功能预测的优化与应用。通过高效的数据处理能力和智能算法,提升基因功能预测的准确性与效率,助力医学与农业发展。
|
2月前
|
Java 大数据 数据处理
Java 大视界 -- 基于 Java 的大数据实时数据处理在工业互联网设备协同制造中的应用与挑战(222)
本文探讨了基于 Java 的大数据实时数据处理在工业互联网设备协同制造中的应用与挑战。文章分析了传统制造模式的局限性,介绍了工业互联网带来的机遇,并结合实际案例展示了 Java 在多源数据采集、实时处理及设备协同优化中的关键技术应用。同时,也深入讨论了数据安全、技术架构等挑战及应对策略。