我们普遍知道的创建线程的方式有两种,一种是继承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为TV端助力 关于Fragment你所需知道的一切!

    转载自刘明渊 的博客地址:http://blog.csdn.net/vanpersie_9987 Fragment 是 Android API 中的一个类,它代表Activity中的一部分界面:您可以 ...

  2. django 与 Vue 的结合使用说明

    1.第一步有一个Django项目 先是创建一个Django项目 django-admin startproject demo 然后创建一个application应用 python manage.py ...

  3. Android P的APP适配总结,让你快人一步

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由QQ音乐技术团队发表于云+社区专栏 上篇:Android P 行为变更适配 Android P 这次有很多行为变更,其中不乏一些需要亟 ...

  4. java的优点和误解 《java核心技术卷i》第一章

    <java核心技术卷i>第一章主要内容包括三点: 1:Java白皮书的关键术语:描述Java的十一个关键字: 2:Java applet 3 :关于Java的常见误解   1:第一章:Ja ...

  5. springboot 学习之路 6(集成durid连接池)

    目录:[持续更新.....] spring 部分常用注解 spring boot 学习之路1(简单入门) spring boot 学习之路2(注解介绍) spring boot 学习之路3( 集成my ...

  6. 修改minifest使桌面软件支持高dpi

    在VisualStudio中可以很方便的设置manifest以支持高dpi的用户界面.当然也可以手工修改manifest文件来添加对高dpi的支持. QQ在高dpi方面做的尤其差,对高dpi的支持迟迟 ...

  7. int,int32_t,int64_t

    一.数据类型特别是int相关的类型在不同位数机器的平台下长度不同.C99标准并不规定具体数据类型的长度大小,只规定级别.作下比较: 16位平台 char         1个字节8位short     ...

  8. CentOS6.5内 MySQL5.7.19编译安装

    作为博主这样的Linux菜鸟,CentOS下最喜欢的就是yum安装.但有时候因为特殊情况(例如被墙等),某些软件可能没办法直接通过yum来安装,这时候我们可以使用编译安装或者直接二进制文件安装. 本博 ...

  9. Python对文件的解压和压缩

    zipfile: 解压: import os, zipfile serverzip_path = 'D:\\server.zip' serverzip_target_path = 'd:\\2' f ...

  10. 阿里云ECS Ubuntu16.0 安装 uwsgi 失败解决方案

    Ubuntu安装包时报错 E:Unable to locate package xxx(如:python3-pip) 一般新安装Ubuntu后需要先更新软件源: apt-get update apt- ...