多线程-Callable、Future、FutureTask
我们普遍知道的创建线程的方式有两种,一种是继承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的更多相关文章
- Java 并发编程——Callable+Future+FutureTask
		Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ... 
- 12 Callable & Future & FutureTask
		创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用 ... 
- java 并发runable,callable,future,futureTask
		转载自:http://www.cnblogs.com/dolphin0520/p/3949310.html package future_call; import java.util.concurre ... 
- Java多线程:Callable,Future,FutureTask
		一.Future Future和Callable基本是成对出现的,Callable负责产生结果,Future负责获取结果. 1.Callable接口类似于Runnable,只是Runnable ... 
- Callable,Future,FutureTask
		1.概念定义 2.实现例子 3.总结 1.概念定义 1.Callable Callable是一个接口,效果类似Runnable接口.实现该接口,然后,耗时操作在call()方法中执行.与Run ... 
- JAVA 多线程 Callable 与 FutureTask:有返回值的多线程
		java多线程中,如果需要有返回值,就需要实现Callable接口. 看例子: 先建立一个Dowork这个类,就是平时某个业务的实现 package com.ming.thread.one; impo ... 
- java callable future futuretask
		Runnbale封装一个异步运行的任务,可以把它想象成一个没有任何参数和返回值的异步方法.Callable和Runnable相似,但是它有返回值.Callable接口是参数化的类型,只有一个方法cal ... 
- paip.java 多线程参数以及返回值Future FutureTask 的使用.
		paip.java 多线程参数以及返回值Future FutureTask 的使用. 在并发编程时,一般使用runnable,然后扔给线程池完事,这种情况下不需要线程的结果. 所以run的返回值是vo ... 
- Callable, Runnable, Future, FutureTask
		Java并发编程之Callable, Runnable, Future, FutureTask Java中存在Callable, Runnable, Future, FutureTask这几个与线程相 ... 
随机推荐
- react 会员登录
			会员登录在我们的好多项目中都有用到,比如在后台管理系统,它的第一步就需要你进行登录,还有在我们常见的京东.淘宝.网易云音乐等一系列的软件上面都需要进行登录. 下面我们直接上代码 fetch(url,{ ... 
- 利用顶点位移进行VR畸变校正
			VR开发的最大挑战之一是对高帧率与高分辨率结合的要求.我们通过把顶点转化为“镜头空间”,删除了需要全屏渲染的纹理,这样就可以大规模提高手机性能. 下面的技术使用谷歌的Cardboard Unity S ... 
- 微信小程序 刷新当前页面
			刷新当前页面 Measure 法一: 需要的地方 this.onLoad(),试过之后,无用!!! 法二:亲测有效 在this.onReady()中进行获取数据操作(这样一来,就既可以在初次显示时直接 ... 
- 解决OpenCV JavaCameraView相机preview方向问题
			网上找了很多解决都是有问题的,研究了半天源码解决了这个问题.我是从整个相机启动和数据传输的过程着手的,这里捡重点介绍一下,最后会贴上修改后的两个源文件. 首先要知道一个概念. 图里的小圆圈是Home按 ... 
- Android Handler、Message、MessageQueue和Looper官方说明
			Handler官方说明 官方API文档:https://developer.android.google.cn/reference/android/os/Handler Handler允许您发送和处理 ... 
- C#方法重载(overload)方法重写(override)隐藏(new)
			一.重载:同一个作用域内发生(比如一个类里面),定义一系列同名方法,但是方法的参数列表不同.这样才能通过传递不同的参数来决定到底调用哪一个. 值得注意的是,方法重载只有通过参数不同来判断调用哪个方法, ... 
- Django之--通过MVC架构的html模板展示Hello World!
			上一篇:Django之--网页展示Hello World! 初步说明了如何使用Django来显示hello world,本文略微进阶下使用html模板来展示hello world~ 首先在mysite ... 
- 查看iPhone电池寿命
			iBackupBot 软件:iBackupBot for iTunes (收费软件) 官网:http://www.icopybot.com/download.htm iBackupBot for iT ... 
- wamp 中安装cakephp Fatal error: You must enable the intl extension to use CakePHP. in XXX
			今天在wamp下安装cakephp3.x的时候,报出这么一条错误:Fatal error: You must enable the intl extension to use CakePHP. in ... 
- February 25th, 2018 Week 9th Sunday
			LIfe is about making an impact, not making an income. 生命在于影响他人,而非赚钱糊口. From Kevin Kruse. You probabl ... 
