我们普遍知道的创建线程的方式有两种,一种是继承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. Android IPC机制(三)使用AIDL实现跨进程方法调用

    上一篇文章中我们介绍了使用Messenger来进行进程间通信的方法,但是我们能发现Messenger是以串行的方式来处理客户端发来的信息,如果有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户 ...

  2. [随时更新][Android]小问题记录

    此文随时更新,旨在记录平时遇到的不值得单独写博客记录的细节问题,当然如果问题有拓展将会另外写博客. 原文地址请保留http://www.cnblogs.com/rossoneri/p/4040314. ...

  3. Sqlserver还原master

    net stop mssqlserver net start mssqlserver /m"SQLCMD" sqlcmd -s xx sqlcmd -s xx -U sa -P x ...

  4. Python 中if __name__ == '__main__': 的作用和原理

    转自https://blog.csdn.net/weixin_42660771/article/details/84035153 1.代码的功能 一个python的文件有两种使用的方法,第一是直接作为 ...

  5. SqlServer误删数据恢复

    误删数据,操作步骤: 第一步: 找到误删的数据库之前备份文件. 第二步: 1,修改数据库备份模式为:大容量日志 2,修改访问限制为:SINGLE_USER(单用户模式) 第三步: 执行sql一条一条执 ...

  6. Windows SDK 8安装失败的绕坑办法

    安装win sdk 8,提示错误:管道正在被关闭. 查看安装log文件,有如下错误: Error 0x800700e8: Failed to write message type to pipe.Er ...

  7. ASP.NET -- 一般处理程序ashx

    ASP.NET  --   一般处理程序ashx 如果在一个html页面向服务器端请求数据,可用ashx作为后台页面处理数据.ashx适合用作数据后台处理,相当于WebForm中的aspx.cs文件或 ...

  8. gcc5.4报错对‘std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()’未定义的引用

    我在编译ligra是遇到了这个问题,网上搜了一遍,发现是了原因https://gcc.gnu.org/onlinedocs/libstdc%2B%2B/manual/using_dual_abi.ht ...

  9. 用惯图形界面的SVNer,如何突破Git----简单教程

    1.使用Git,首先安装好Git,它会赠送一个Git Bash给你 2.接下来,踩第一个坑----SSH连接,我们知道用Git关联本地仓库可以用SSH和HTTP两种方式,为什么不用HTTP,因为 不! ...

  10. UVA10384-The Wall Pushers(迭代加深搜索)

    Problem UVA10384-The Wall Pushers Accept: 199   Submit: 1546Time Limit: 10000 mSec Problem Descripti ...