在传统的多线程实现方式中(继承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. 开源项目Bug悬赏任务

    导读 2014 年开源加密库 OpenSSL 项目爆出的高危漏洞 Heartblood 让世人意识到一些鲜为人知的开源项目对整个互联网和其它基础设施的完整性和可靠性至关重要,随后 Linux 基金会发 ...

  2. PAT A1108 Finding Average (20 分)——字符串,字符串转数字

    The basic task is simple: given N real numbers, you are supposed to calculate their average. But wha ...

  3. AI Factorization Machine(FM)算法

    FM算法 参考链接: https://www.csie.ntu.edu.tw/~b97053/paper/Rendle2010FM.pdf

  4. 【UVA 11404】Palindromic Subsequence

    UVA 11404 我用了最暴力的做法:考虑\(dp[i][j]\)表示\(S[i..j]\)的最长回文子序列的长度以及字典序最小的那个. 然后转移的时候如下处理:首先\(dp[i][j]\)要取\( ...

  5. Linux命令——find/grep

    这两个命令写起来会很多,这里只简单的写一些东西,加深自己的印象. 一.find find命令主要作用是沿着文件层次结构向下遍历,匹配符合条件的文件,并执行相应的操作. 1)命令格式 find [参数] ...

  6. linux中断源码分析 - 概述(一)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 关于中断和异常 一般在书中都会把中断和异常一起说明,因为它们具有相同的特点,同时也有不同的地方.在CPU里,中断 ...

  7. 如何屏蔽SkylineGlobe提供的三维地图控件上的快捷键

    SkyllineGlobe提供的 <OBJECT ID=" TerraExplorer3DWindow" CLASSID="CLSID:3a4f9192-65a8- ...

  8. FileShare枚举的使用(文件读写锁) - (转载)

    开发过程中,我们往往需要大量与文件交互,但往往会出现很多令人措手不及的意外,所以对普通的C#文件操作做了一次总结,问题大部分如下: 写入一些内容到某个文件中,在另一个进程/线程/后续操作中要读取文件内 ...

  9. (转)deb制作文件详解

    转自:http://blog.chinaunix.net/uid-16184599-id-3041024.html 如何制作Deb包和相应的软件仓库,其实这个很简单.这里推荐使用dpkg来进行deb包 ...

  10. [转]The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path

    完整错误信息: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS"AS IS" AND ANY ...