我们普遍知道的创建线程的方式有两种,一种是继承Thread,一种是实现Runnable接口。这两种方式都无法获取任务执行完后的结果,并发包提供了Callable 类能够得到任务执行完的结果。

为何需要Future与Callable的模式?我们先用常用方式来实现需求。获取线程执行完后的结果。

public class Demo1 {
public static void main(String[] args) {
Callable callable = new Callable() {
@Override
public void call(int num) {
System.out.println("线程运行结果:"+num);
}
};
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程:"+Thread.currentThread().getName()+"开始");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
callable.call(100);
System.out.println("线程:"+Thread.currentThread().getName()+"结束");
}
},"t1").start();
System.out.println("主线程结束");
}
}
interface Callable{
void call(int num);
}

 一般我们使用回调的方式来获取结果。但这种方式有三个缺点:

  1、必须要有回调接口。并根据线程运行情况,接口至少要拥有成功回调和错误回调两个方法。

  2、当线程运行后,只能等到线程回调接口,开发者没办法进行取消操作。

  3、如果要重复获取同样线程运行结果,只能重新运行线程。或缓存一个变量结果值。

  因此java提供了Future与Callable的模式


Callable:

  位于java.util.concurrent包下的一个接口,声明了一个call()方法。

public interface Callable<V> {
// 计算结果,如果无法计算结果,则抛出一个异常
V call() throws Exception;
}

   Callable一般与ExecutorService配合使用。在ExecutorService接口中声明了几个submit方法的重载:

//提交一个实现Callable接口的任务,并且返回封装了异步计算结果的Future
<T> Future<T> submit(Callable<T> task);
//提交一个实现Runnable接口的任务,并且指定了在调用Future的get方法时返回的result对象
<T> Future<T> submit(Runnable task, T result);
//提交一个实现Runnable接口的任务,并且返回封装了异步计算结果的Future
Future<?> submit(Runnable task);

  对比Runnable:

Callable与Runnable的区别:
 
(1)Callable规定的方法是call(),而Runnable规定的方法是run()。 (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。 (3)call()方法可抛出异常,而run()方法是不能抛出异常的。 (4)运行Callable任务可拿到一个Future对象。 (5) 加入线程池运行时,Runnable使用的是ExecutorService的execute方法,而Callable使用的submit方法。 

Future:

  Future可以对具体的Runnable或Callable任务的执行结果进行取消,查询是否完成、获取结果。可通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

  Future是位于java.util.concurrent包下的一个接口:

public interface Future<V> {
// 试图取消对此任务的执行,参数表示是否允许取消正在执行却未执行完的任务
boolean cancel(boolean mayInterruptIfRunning);
// 如果在任务正常完成前将其取消,则返回true
boolean isCancelled();
// 如果任务已完成,则返回true
boolean isDone();
// 获取执行结果,产生阻塞直到任务执行完毕才返回
V get() throws InterruptedException, ExecutionException;
// 用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}

  Futre提供的三种功能:判断任务是否完成、能够中断任务、能够获取 任务结果。Future是接口,无法直接创建对象来使用,因此就有了FutureTask。


FutureTask:

  FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable接口和Future接口。所以它可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。它是Future接口的一个唯一实现类。

  FutureTask的状态:

    1、未启动:当FutureTask.run()方法未被执行前,处于未启动状态。当创建一个FutureTask但run方法未执行前,也处于未启动状态。

    2、已启动:FutureTask.run()被执行的过程中,处于已启动状态。

    3、已完成:FutureTask.run()执行完正常结束或被取消或抛出异常而结束,都处于完成状态。

  FutureTask的执行:

  

    1、未启动或已启动状态下,执行FutureTask.get()方法会导致调用线程阻塞。已完成状态下,执行该方法调用线程会立即返回结果或抛出异常。

    2、未启动状态下,执行FutureTask.cancel()方法将导致任务永不执行。已启动状态下,执行cancel(true)方法将以中断执行此任务线程的方式来试图停止任务。取消成功返回true。但如果执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时cancel(...)返回false。当任务已经完成,执行cancel(...)方法将返回false。


使用实例:

Callable实现类

/**
* @Title: CallableServiceImpl
* @Description: Callable实现类
* @date 2019/1/1814:18
*/
public class CallableServiceImpl implements Callable<Integer> {
private static final Logger logger = LoggerFactory.getLogger(CallableServiceImpl.class);
private int sum;
@Override
public Integer call() throws Exception {
logger.info("callable子线程开始计算");
Thread.sleep(2000);
for (int i = 0; i < 100; i++) {
sum = sum + i;
}
logger.info("callable子线程结束计算");
return sum;
}
}

Callable+Future

/**
* @Title: FutureClient
* @Description:
* @date 2019/1/1814:21
*/
public class FutureClient {
private static final Logger logger = LoggerFactory.getLogger(FutureClient.class); public static void main(String[] args) {
//创建线程池
ExecutorService es = Executors.newSingleThreadExecutor();
//创建任务
CallableServiceImpl task = new CallableServiceImpl();
//提交任务并获取执行结果
Future<Integer> future = es.submit(task);
//关闭线程池
es.shutdown();
try {
Thread.sleep(2000);
if (future.get()!=null){
logger.info("结果是:"+future.get());
}else {
logger.info("未获取到结果");
}
} catch (Exception e) {
logger.error("FutureClient error {}",e);
}
logger.info("主线程执行完成");
}
}

Callable+FutureTask

/**
* @Title: FutureTaskClient
* @Description:
* @date 2019/1/1814:26
*/
public class FutureTaskClient {
private static final Logger logger = LoggerFactory.getLogger(FutureTaskClient.class); public static void main(String[] args) {
//创建线程池
ExecutorService es = Executors.newSingleThreadExecutor();
//创建任务
CallableServiceImpl task = new CallableServiceImpl();
//提交任务
FutureTask<Integer> futureTask = new FutureTask<>(task);
es.submit(futureTask);
//关闭线程池
es.shutdown();
try {
Thread.sleep(2000);
if (futureTask.get()!=null){
logger.info("结果是:"+futureTask.get());
}else {
logger.info("未获取到结果");
}
} catch (Exception e) {
logger.error("FutureTaskClient error {}",e);
}
logger.info("主线程执行完成");
}
}

  

多线程-Callable、Future、FutureTask的更多相关文章

  1. Java 并发编程——Callable+Future+FutureTask

    Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...

  2. 12 Callable & Future & FutureTask

    创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用 ...

  3. java 并发runable,callable,future,futureTask

    转载自:http://www.cnblogs.com/dolphin0520/p/3949310.html package future_call; import java.util.concurre ...

  4. Java多线程:Callable,Future,FutureTask

    一.Future Future和Callable基本是成对出现的,Callable负责产生结果,Future负责获取结果.     1.Callable接口类似于Runnable,只是Runnable ...

  5. Callable,Future,FutureTask

    1.概念定义 2.实现例子 3.总结   1.概念定义   1.Callable Callable是一个接口,效果类似Runnable接口.实现该接口,然后,耗时操作在call()方法中执行.与Run ...

  6. JAVA 多线程 Callable 与 FutureTask:有返回值的多线程

    java多线程中,如果需要有返回值,就需要实现Callable接口. 看例子: 先建立一个Dowork这个类,就是平时某个业务的实现 package com.ming.thread.one; impo ...

  7. java callable future futuretask

    Runnbale封装一个异步运行的任务,可以把它想象成一个没有任何参数和返回值的异步方法.Callable和Runnable相似,但是它有返回值.Callable接口是参数化的类型,只有一个方法cal ...

  8. paip.java 多线程参数以及返回值Future FutureTask 的使用.

    paip.java 多线程参数以及返回值Future FutureTask 的使用. 在并发编程时,一般使用runnable,然后扔给线程池完事,这种情况下不需要线程的结果. 所以run的返回值是vo ...

  9. Callable, Runnable, Future, FutureTask

    Java并发编程之Callable, Runnable, Future, FutureTask Java中存在Callable, Runnable, Future, FutureTask这几个与线程相 ...

随机推荐

  1. drawable自定义字体颜色

    一个很基础简单的问题,但是以前没用过,都是代码控制效果的,最近新的项目发现设置了color属性没效果,后来查了会资料才发现得单独设置,记录一下,虽然是小问题 上面的xml控制背景的变化,一开始我设置在 ...

  2. Python random模块方法

    random内置模块中的方法注解 random.seed(a=None, version=2) # 初始化伪随机数生成器,若种子a相同,则可以使生成的随机数相同.如果未提供a或者a=None,则使用系 ...

  3. Linux 最小化安装后IP的配置(手动获取静态IP地址)

    一.图形化界面配置(假设为电脑A) 如果你的Linux安装有图形化界面,那么通过以下方式来配置: 我这里是有两块网卡,第一个网卡在上篇中已经通过DHCP来配置了:Linux 最小化安装后IP的配置(D ...

  4. KMP算法详解-彻底清楚了(转载+部分原创)

    引言 KMP算法指的是字符串模式匹配算法,问题是:在主串T中找到第一次出现完整子串P时的起始位置.该算法是三位大牛:D.E.Knuth.J.H.Morris和V.R.Pratt同时发现的,以其名字首字 ...

  5. Django之--MVC的Model

    在上一篇:Django之--通过MVC架构的html模板展示Hello World! 讲述了基本的MVC模型,但是却并没有测试Model的作用,本文通过mysql数据库来测试. Django自带的mo ...

  6. 分组统计SQL

    Itpub上遇到一个求助写SQL的帖子,感觉很有意思,于是写出来看看,要求如下: 有个计划表1, 记录物料的年度计划量 有个实际使用情况表2,记录实际使用情况. 最后要出个统计表,把计划和实际的数据结 ...

  7. Win10安装sqlserver2014打开显示黑色界面,mardown打开显示报错

    问题描述: 我电脑从win7更新到win10以后就打开sqlserver2014显示黑色背景有问题,卸载了又装都是没有用 然后我又发现mardown也是有问题打开报告什么错误,忘记截图了,去网上找了个 ...

  8. Java访问级别修饰符

    用途 控制其他类可以访问的字段或方法 修饰符 public.protected.no modifier(未声明).private 访问级别 修饰符 当前类 包 子类 其他包 public √ √ √ ...

  9. 【转】win2008 中iis7设置404页面但返回状态200的问题解决办法

    今天根据SEO反馈,某个站点中设置的404页面返回的http状态为200.通过站长工具进行查询,发现返回的状态确实为200. 通过彻查问题,发现这个网站的服务器环境为windows2008 服务器为i ...

  10. spark基础知识

    1.Spark是什么? UCBerkeley AMPlab所开源的类HadoopMapReduce的通用的并行计算框架. dfsSpark基于mapreduce算法实现的分布式计算,拥有HadoopM ...