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高速轮询的方式获取任务结果,但这种方式比较耗费资源。不建议使用

目录
相关文章
|
2月前
|
人工智能 安全 Java
Java和Python在企业中的应用情况
Java和Python在企业中的应用情况
64 7
|
2月前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
179 3
|
14天前
|
安全 算法 Java
Java CAS原理和应用场景大揭秘:你掌握了吗?
CAS(Compare and Swap)是一种乐观锁机制,通过硬件指令实现原子操作,确保多线程环境下对共享变量的安全访问。它避免了传统互斥锁的性能开销和线程阻塞问题。CAS操作包含三个步骤:获取期望值、比较当前值与期望值是否相等、若相等则更新为新值。CAS广泛应用于高并发场景,如数据库事务、分布式锁、无锁数据结构等,但需注意ABA问题。Java中常用`java.util.concurrent.atomic`包下的类支持CAS操作。
45 2
|
2月前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
172 6
|
1月前
|
监控 Java 数据库连接
Java线程管理:守护线程与用户线程的区分与应用
在Java多线程编程中,线程可以分为守护线程(Daemon Thread)和用户线程(User Thread)。这两种线程在行为和用途上有着明显的区别,了解它们的差异对于编写高效、稳定的并发程序至关重要。
37 2
|
2月前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
70 6
|
2月前
|
关系型数据库 MySQL Java
MySQL索引优化与Java应用实践
【11月更文挑战第25天】在大数据量和高并发的业务场景下,MySQL数据库的索引优化是提升查询性能的关键。本文将深入探讨MySQL索引的多种类型、优化策略及其在Java应用中的实践,通过历史背景、业务场景、底层原理的介绍,并结合Java示例代码,帮助Java架构师更好地理解并应用这些技术。
58 2
|
2月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
2月前
|
Java 测试技术 API
Java 反射机制:深入解析与应用实践
《Java反射机制:深入解析与应用实践》全面解析Java反射API,探讨其内部运作原理、应用场景及最佳实践,帮助开发者掌握利用反射增强程序灵活性与可扩展性的技巧。
127 4
|
2月前
|
Java BI API
Java Excel报表生成:JXLS库的高效应用
在Java应用开发中,经常需要将数据导出到Excel文件中,以便于数据的分析和共享。JXLS库是一个强大的工具,它基于Apache POI,提供了一种简单而高效的方式来生成Excel报表。本文将详细介绍JXLS库的使用方法和技巧,帮助你快速掌握Java中的Excel导出功能。
75 6