futuretask用法及使用场景介绍

FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。

1. FutureTask执行多任务计算的使用场景

利用FutureTask和ExecutorService,可以用多线程的方式提交计算任务,主线程继续执行其他任务,当主线程需要子线程的计算结果时,在异步获取子线程的执行结果。

package futuretask;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class FutureTaskForMultiCompute {
  public static void main(String[] args) {
    FutureTaskForMultiCompute inst=new FutureTaskForMultiCompute();
    // 创建任务集合
    List<FutureTask<Integer>> taskList = new ArrayList<FutureTask<Integer>>();
    // 创建线程池
    ExecutorService exec = Executors.newFixedThreadPool(5);
    for (int i = 0; i < 10; i++) {
      // 传入Callable对象创建FutureTask对象
      FutureTask<Integer> ft = new FutureTask<Integer>(inst.new ComputeTask(i, ""+i));
      taskList.add(ft);
      // 提交给线程池执行任务,也可以通过exec.invokeAll(taskList)一次性提交所有任务;
      exec.submit(ft);
    }
    System.out.println("所有计算任务提交完毕, 主线程接着干其他事情!");
    // 开始统计各计算线程计算结果
    Integer totalResult = 0;
    for (FutureTask<Integer> ft : taskList) {
      try {
        //FutureTask的get方法会自动阻塞,直到获取计算结果为止
        totalResult = totalResult + ft.get();
      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (ExecutionException e) {
        e.printStackTrace();
      }
    }
    // 关闭线程池
    exec.shutdown();
    System.out.println("多任务计算后的总结果是:" + totalResult);
  }
  private class ComputeTask implements Callable<Integer> {
    private Integer result = 0;
    private String taskName = "";
    public ComputeTask(Integer iniResult, String taskName){
      result = iniResult;
      this.taskName = taskName;
      System.out.println("生成子线程计算任务: "+taskName);
    }
    public String getTaskName(){
      return this.taskName;
    }
    @Override
    public Integer call() throws Exception {
      // TODO Auto-generated method stub
      for (int i = 0; i < 100; i++) {
        result =+ i;
      }
      // 休眠5秒钟,观察主线程行为,预期的结果是主线程会继续执行,到要取得FutureTask的结果是等待直至完成。
      Thread.sleep(5000);
      System.out.println("子线程计算任务: "+taskName+" 执行完成!");
      return result;
    }
  }
} 

2. FutureTask在高并发环境下确保任务只执行一次

在很多高并发的环境下,往往我们只需要某些任务只执行一次。这种使用情景FutureTask的特性恰能胜任。举一个例子,假设有一个带key的连接池,当key存在时,即直接返回key对应的对象;当key不存在时,则创建连接。对于这样的应用场景,通常采用的方法为使用一个Map对象来存储key和连接池对应的对应关系,典型的代码如下面所示:

private Map<String, Connection> connectionPool = new HashMap<String, Connection>();
private ReentrantLock lock = new ReentrantLock();
public Connection getConnection(String key){
  try{
    lock.lock();
    if(connectionPool.containsKey(key)){
      return connectionPool.get(key);
    }
    else{
      //创建 Connection
      Connection conn = createConnection();
      connectionPool.put(key, conn);
      return conn;
    }
  }
  finally{
    lock.unlock();
  }
}
//创建Connection
private Connection createConnection(){
  return null;
} 

在上面的例子中,我们通过加锁确保高并发环境下的线程安全,也确保了connection只创建一次,然而确牺牲了性能。改用ConcurrentHash的情况下,几乎可以避免加锁的操作,性能大大提高,但是在高并发的情况下有可能出现Connection被创建多次的现象。这时最需要解决的问题就是当key不存在时,创建Connection的动作能放在connectionPool之后执行,这正是FutureTask发挥作用的时机,基于ConcurrentHashMap和FutureTask的改造代码如下:

private ConcurrentHashMap<String,FutureTask<Connection>>connectionPool = new ConcurrentHashMap<String, FutureTask<Connection>>();
public Connection getConnection(String key) throws Exception{
  FutureTask<Connection>connectionTask=connectionPool.get(key);
  if(connectionTask!=null){
    return connectionTask.get();
  }
  else{
    Callable<Connection> callable = new Callable<Connection>(){
      @Override
      public Connection call() throws Exception {
        // TODO Auto-generated method stub
        return createConnection();
      }
    };
    FutureTask<Connection>newTask = new FutureTask<Connection>(callable);
    connectionTask = connectionPool.putIfAbsent(key, newTask);
    if(connectionTask==null){
      connectionTask = newTask;
      connectionTask.run();
    }
    return connectionTask.get();
  }
}
//创建Connection
private Connection createConnection(){
  return null;
} 

经过这样的改造,可以避免由于并发带来的多次创建连接及锁的出现。

总结

以上就是本文关于futuretask用法及使用场景介绍的全部内容,希望对大家有所帮助。感兴趣的朋友可以参阅:浅谈Java多线程处理中Future的妙用(附源码)、Java利用future及时获取多线程运行结果、Java多线程ForkJoinPool实例详解等,有什么问题可以随时留言,欢迎各位参阅本站其他相关专题。

(0)

相关推荐

  • java多线程返回值使用示例(callable与futuretask)

    Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值,下面来看一个简单的例子 复制代码 代码如下: package com.future.test; import java.io.FileNotFoundException;import java.io.IOException;i

  • futuretask源码分析(推荐)

    FutureTask只实现RunnableFuture接口: 该接口继承了java.lang.Runnable和Future接口,也就是继承了这两个接口的特性. 1.可以不必直接继承Thread来生成子类,只要实现run方法,且把实例传入到Thread构造函数,Thread就可以执行该实例的run方法了( Thread(Runnable) ). 2.可以让任务独立执行,get获取任务执行结果时,可以阻塞直至执行结果完成.也可以中断执行,判断执行状态等. FutureTask是一个支持取消行为的异

  • Java中的Runnable,Callable,Future,FutureTask的比较

    Java中的Runnable,Callable,Future,FutureTask的比较 Java中存在Runnable.Callable.Future.FutureTask这几个与线程相关的类或者接口,在Java中也是比较重要的几个概念,我们通过下面的简单示例来了解一下它们的作用于区别. Runnable 其中Runnable应该是我们最熟悉的接口,它只有一个run()函数,用于将耗时操作写在其中, 该函数没有返回值 .然后使用某个线程去执行该runnable即可实现多线程,Thread类在调

  • futuretask用法及使用场景介绍

    FutureTask可用于异步获取执行结果或取消执行任务的场景.通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果.另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTa

  • Vue.set与this.$set的用法与使用场景介绍

    目录 Vue.set()和this.$set()介绍: 一.为什么有Vue.set 二.解决方法 数组 对象 三.Vue.set 对于数组 对于对象 注意 四.使用场景 Vue.set()和this.$set()介绍: 在我们使用vue进行开发的过程中,可能会遇到一种情况:当生成vue实例后,当再次给数据赋值时,有时候并不会自动更新到视图上去:当我们去看vue文档的时候,会发现有这么一句话:如果在实例创建之后添加新的属性到实例上,它不会触发视图更新. 一.为什么有Vue.set 由于JavaSc

  • Java redis使用场景介绍

    目录 1.作为缓存 1.1 为何使用 1.2 什么样的数据适合放入缓存 1.3 使用redis作为缓存 1.3.1 未使用配置类 1.3.2 使用配置类 2.分布式锁 2.1 压测工具的使用 2.2 库存项目 2.2.1 controller层 2.2.2 dao层 2.2.3 entity层 2.2.4 service层 2.2.5 mapper 2.2.6 依赖 2.2.7 测试结果 2.3 解决方案 2.3.1 使用 synchronized 或者lock锁 2.3.2 使用redisTe

  • React使用ref方法与场景介绍

    目录 摘要 1.ref的挂载 2.使用ref的三种方式 3.ref的使用场景 摘要 不管在Vue中还是React,如果我们想使用一个元素的DOM,不需要通过JS中操纵DOM的方法,它们提供了一个专属的API就是ref. 而Vue中的ref可能比较简单,这一篇主要讲一下如何在React中使用ref,以及使用ref的场景. 1.ref的挂载 在React中,ref可以挂载到html元素上,同时也可以挂载在React元素上,看下面的代码: import React, { Component } fro

  • 中文版ChatGPT文心一言使用场景介绍

    目录 介绍 5 个使用场景 对比 Bing Chat 和 ChatGPT 使用 介绍 文心一言(英文名:ERNIE Bot)是百度全新一代基于飞桨深度学习平台和文心知识增强大语言模型,能够与人对话互动,回答问题,协助创作. 3月16日正式在百度总部 “挥手点江山” 发布. 李彦宏展示了文心一言在文学创作.商业文案创作.数理逻辑推算.中文理解.多模态生成五个使用场景中的综合能力. 王海峰解读了文心一言的技术特性及其背后的技术积累,表示文心一言是在ERNIE及PLATO系列模型的基础上研发的. 在人

  • Redis中5种数据结构的使用场景介绍

    一.redis 数据结构使用场景 原来看过 redisbook 这本书,对 redis 的基本功能都已经熟悉了,从上周开始看 redis 的源码.目前目标是吃透 redis 的数据结构.我们都知道,在 redis 中一共有5种数据结构,那每种数据结构的使用场景都是什么呢? String--字符串 Hash--字典 List--列表 Set--集合 Sorted Set--有序集合 下面我们就来简单说明一下它们各自的使用场景: 1. String--字符串 String 数据结构是简单的 key-

  • Redis数据库的应用场景介绍

    一.MySql+Memcached架构的问题 实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache,加速访问,很多公司都曾经使用过这样的架构,但随着业务数据量的不断增加,和访问量的持续增长,我们遇到了很多问题: 1)MySQL需要不断进行拆库拆表,Memcached也需不断跟着扩容,扩容和维护工作占据大量开发时间. 2)Memcached与MySQL数据库数据一致性问题. 3)Memcached数据命中率低或down机,大量访问直接穿透到DB,MySQL无法支

  • Node.js的特点和应用场景介绍

    Node.js应该是当今最火热的技术之一.本文主要介绍Node.js的特点及应用场景. Node.js是一个基于Chrome JavaScript运行时建立的一个平台,用来方便地搭建快速的 易于扩展的网络应用.Node.js借助事件驱动,非阻塞I/O模型变得轻量和高效,非常适合 运行在分布式设备的数据密集型实时应用. 1. 特点 1.1 异步I/O 所谓的异步I/O,是相对同步I/O而言的.程序执行过程中必然要进行很多I/O操作,如读写文件.输入输出.请求响应等等.通常来说,I/O操作是非常费时

  • 实例解析ES6 Proxy使用场景介绍

    ES6 中的箭头函数.数组解构.rest 参数等特性一经实现就广为流传,但类似 Proxy 这样的特性却很少见到有开发者在使用,一方面在于浏览器的兼容性,另一方面也在于要想发挥这些特性的优势需要开发者深入地理解其使用场景.就我个人而言是非常喜欢 ES6 的 Proxy,因为它让我们以简洁易懂的方式控制了外部对对象的访问.在下文中,首先我会介绍 Proxy 的使用方式,然后列举具体实例解释 Proxy 的使用场景. Proxy,见名知意,其功能非常类似于设计模式中的代理模式,该模式常用于三个方面:

  • 消息队列应用场景介绍

    一.什么是队列 队列(Queue)是一种常见的数据结构,其最大的特点就是先进先出(First In First Out),作为最基础的数据结构,队列应用很广泛.比如火车站排队买票等等.可以用下图表示队列: 其中a1.a2.an表示队列中的数据.数据从队尾入队列,然后从队头出队列. 二.什么是消息队列 消息队列(Message Queue)是一种使用队列(Queue)作为底层存储数据结构,可以用于解决不同进程与应用程序之间通讯的分布式消息容器,也可以称为消息中间件. 目前比较常用的消息队列有Act

随机推荐