在传统的多线程实现方式中(继承Thread和实现Runnable)无法直接获取线程执行的返回结果,如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。

从Java 1.5开始,java.util.concurrent包中提供了 Callable和 Future两个接口,通过它们就可以在任务执行完毕之后得到任务执行结果。

Callable

Callable与Runnable的功能大致相似,Callable中有一个call()函数,但是call()函数有返回值,而Runnable的run()函数不能将结果返回给客户程序。

Future

Executor就是Runnable和Callable的调度容器,Future就是对于具体的Runnable或者Callable任务的执行结果进行

取消、查询是否完成、获取结果、设置结果操作。get方法会阻塞,直到任务返回结果。

public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
  • cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
  • isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
  • isDone方法表示任务是否已经完成,若任务完成,则返回true;
  • get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
  • get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

也就是说Future提供了三种功能:

  • 判断任务是否完成
  • 能够中断任务
  • 能够获取任务的执行结果

Future和FutureTask区别

Future是一个接口, FutureTask类是Future 的一个实现类,并实现了Runnable,因此FutureTask可以传递到线程对象Thread中新建一个线程执行。所以可通过Excutor(线程池) 来执行,也可传递给Thread对象执行。如果在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。 

FutureTask是为了弥补Thread的不足而设计的,它可以让程序员准确地知道线程什么时候执行完成并获得到线程执行完成后返回的结果(如果有需要)。

FutureTask是一种可以取消的异步的计算任务。它的计算是通过Callable实现的,它等价于可以携带结果的Runnable,并且有三个状态:等待、运行和完成。完成包括所有计算以任意的方式结束,包括正常结束、取消和异常。

Executor框架利用FutureTask来完成异步任务,并可以用来进行任何潜在的耗时的计算。一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。

FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果

Example1

package cn.com.example.concurrent.future;

import java.util.concurrent.*;

/**
* Created by Jack on 2017/1/24.
*/
public class RunnableFutureTask { /**
* ExecutorService
*/
static ExecutorService mExecutor = Executors.newSingleThreadExecutor(); /**
* @param args
*/
public static void main(String[] args) {
runnableDemo();
futureDemo();
} /**
* runnable, 无返回值
*/
static void runnableDemo() { new Thread(new Runnable() { @Override
public void run() {
System.out.println("runnable demo : " + fibc(20));
}
}).start();
} /**
* 其中Runnable实现的是void run()方法,无返回值;Callable实现的是 V
* call()方法,并且可以返回执行结果。其中Runnable可以提交给Thread来包装下
* ,直接启动一个线程来执行,而Callable则一般都是提交给ExecuteService来执行。
*/
static void futureDemo() {
try {
/**
* 提交runnable则没有返回值, future没有数据
*/
Future<?> result = mExecutor.submit(new Runnable() {
@Override
public void run() {
fibc(20);
}
}); System.out.println("future result from runnable : " + result.get()); /**
* 提交Callable, 有返回值, future中能够获取返回值
*/
Future<Integer> result2 = mExecutor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return fibc(20);
}
}); System.out.println("future result from callable : " + result2.get()); /**
* FutureTask则是一个RunnableFuture<V>,即实现了Runnbale又实现了Futrue<V>这两个接口,
* 另外它还可以包装Runnable(实际上会转换为Callable)和Callable
* <V>,所以一般来讲是一个符合体了,它可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行
* ,并且还可以通过v get()返回执行结果,在线程体没有执行完成的时候,主线程一直阻塞等待,执行完则直接返回结果。
*/
FutureTask<Integer> futureTask = new FutureTask<Integer>(
new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return fibc(20);
}
});
// 提交futureTask
mExecutor.submit(futureTask);
System.out.println("future result from futureTask : " + futureTask.get()); } catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
} /**
* 效率底下的斐波那契数列, 耗时的操作
*
* @param num
* @return
*/
static int fibc(int num) {
if (num == 0) {
return 0;
}
if (num == 1) {
return 1;
}
return fibc(num - 1) + fibc(num - 2);
} }

结果:

runnable demo : 6765
future result from runnable : null
future result from callable : 6765
future result from futureTask : 6765

Example2:

package cn.com.example.concurrent.future;

import java.util.Random;
import java.util.concurrent.*; /**
* Created by Jack on 2017/1/24.
*/
public class RunnableFutureTask { public static void main(String[] args) {
// 初始化一个Callable对象和FutureTask对象
Callable pAccount = new PrivateAccount();
FutureTask futureTask = new FutureTask(pAccount);
// 使用futureTask创建一个线程
Thread pAccountThread = new Thread(futureTask);
System.out.println("futureTask线程现在开始启动,启动时间为:" + System.nanoTime());
pAccountThread.start();
System.out.println("主线程开始执行其他任务");
// 从其他账户获取总金额
int totalMoney = new Random().nextInt(100000);
System.out.println("现在你在其他账户中的总金额为" + totalMoney);
System.out.println("等待私有账户总金额统计完毕...");
// 测试后台的计算线程是否完成,如果未完成则等待
while (!futureTask.isDone()) {
try {
Thread.sleep(500);
System.out.println("私有账户计算未完成继续等待...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("futureTask线程计算完毕,此时时间为" + System.nanoTime());
Integer privateAccountMoney = null;
try {
privateAccountMoney = (Integer) futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("您现在的总金额为:" + totalMoney + privateAccountMoney.intValue());
}
} class PrivateAccount implements Callable {
Integer totalMoney; @Override
public Object call() throws Exception {
Thread.sleep(5000);
totalMoney = new Integer(new Random().nextInt(10000));
System.out.println("您当前有" + totalMoney + "在您的私有账户中");
return totalMoney;
} }

结果:

futureTask线程现在开始启动,启动时间为:88171383410225
主线程开始执行其他任务
现在你在其他账户中的总金额为2838
等待私有账户总金额统计完毕...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
私有账户计算未完成继续等待...
您当前有9238在您的私有账户中
私有账户计算未完成继续等待...
futureTask线程计算完毕,此时时间为88176389195218
您现在的总金额为:28389238

java.util.concuttent Callable Future详解的更多相关文章

  1. java.util.logging.Logger使用详解 (转)

    http://lavasoft.blog.51cto.com/62575/184492/ ************************************************* java. ...

  2. java.util.ResourceBundle国际化用法详解

    java.util.ResourceBundle国际化用法详解 初识国际化和ResourceBundle 这个类主要用来解决国际化和本地化问题.国际化和本地化可不是两个概念,两者都是一起出现的.可以说 ...

  3. java.util.logging.Logger 使用详解

    概述: 第1部分 创建Logger对象 第2部分 日志级别 第3部分 Handler 第4部分 Formatter 第5部分 自定义 第6部分 Logger的层次关系 参考 第1部分 创建Logger ...

  4. java.util.concurrent.atomic 包详解

    Atomic包的作用: 方便程序员在多线程环境下,无锁的进行原子操作 Atomic包核心: Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作 关于CAS compar ...

  5. java.util.logging.Logger使用详解

    一.创建Logger对象   static Logger getLogger(String name)           为指定子系统查找或创建一个 logger. static Logger ge ...

  6. java.util.ConcurrentModificationException 异常问题详解

    环境:JDK 1.8.0_111 在Java开发过程中,使用iterator遍历集合的同时对集合进行修改就会出现java.util.ConcurrentModificationException异常, ...

  7. 2.java.util.logging.Logger使用详解

    一.java.util.logging.Logger简介 java.util.logging.Logger不是什么新鲜东西了,1.4就有了,可是因为log4j的存在,这个logger一直沉默着, 其实 ...

  8. java.util.logging jdk日志详解

    jdk自带的日志,结构并不复杂,功能也能满足绝大部分功能.日志写入位置是开放的,只要继承了handler都可以接收日志的写入.handler本身依赖于LogRecord对象,该对象代表一个日志.Han ...

  9. Java网络编程和NIO详解9:基于NIO的网络编程框架Netty

    Java网络编程和NIO详解9:基于NIO的网络编程框架Netty 转自https://sylvanassun.github.io/2017/11/30/2017-11-30-netty_introd ...

随机推荐

  1. zabbix学习-如何部署一个agent客户端

    1. 部署一个agent客户端很简单,比如监控服务器本身 yum install zabbix-agent -y 2.配置文件位置: vim /etc/zabbix/zabbix-agendt.con ...

  2. nginx的反向代理proxy_pass指令

    1. 首先什么是代理服务器?客户机发送请求时,不会直接发送到目的主机,而是先被代理服务器收到,代理服务器收到客服机的请求后,再向目的机发出,目的机就会返回数据给客户机,在返回给客户机之前,会被代理服务 ...

  3. oracle 相除后保留指定位数小数round()

    ) xxx from dual; XXX----------    3.8871

  4. VC++编写简单串口上位机程序

    VC++编写简单串口上位机程序   转载: http://blog.sina.com.cn/s/articlelist_1809084904_0_1.html VC++编写简单串口上位机程序 串口通信 ...

  5. Luogu4546 THUWC2017 在美妙的数学王国中畅游 LCT、泰勒展开

    传送门 题意:反正就是一堆操作 LCT总是和玄学东西放在一起我们不妨令$x_0=0.5$(其实取什么都是一样的,但是最好取在$[0,1]$的范围内),将其代入给出的式子,我们得到的$f(x)$的式子就 ...

  6. zjoi2018 day1游记

    咕咕咕 upd:看见有人贴上zhihu的问题,那个我早就看到了... 谴责一番题主 @gzy_cjoier 阅读量马上700没想到吧 既然这么火我挂个广告吧 永别,OI 听说有人催更??

  7. Django后端彻底解决跨域问题

    最近在接一个前后端分离的项目,后端使用的django-restframework,前端使用的Vue.后端跑起来后,发现前端在访问后端API时出了了跨域的问题. 类似如下报错: 关于跨域问题,之前这篇文 ...

  8. 用JS制作一个信息管理平台(1)

    首先,介绍一些需要用到的基本知识. [JSON] JSON是数据交互中,最常用的一种数据格式. 由于各种语言的语法都不相同,在传递数据时,可以将自己语言中的数组.对象等转换为JSON字符串. 传递之后 ...

  9. Nagios图像绘制插件PNP4Nagios部署和测试

    注:本篇博客Nagios版本Nagios-3.5.1 1. 概述2. 关于PNP4Nagios3. 部署PNP4Nagios3.1 下载PNP4Nagios3.2 编译安装3.3 目录文件说明4. 配 ...

  10. if...else 小练习

    # 需求:猜年龄,可以让用户最多猜三次 age = 60 for i in range(3): guess = int(input("Input Age: ")) if guess ...