多线程-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这几个与线程相 ...
随机推荐
- Android为TV端助力 关于Fragment你所需知道的一切!
转载自刘明渊 的博客地址:http://blog.csdn.net/vanpersie_9987 Fragment 是 Android API 中的一个类,它代表Activity中的一部分界面:您可以 ...
- django 与 Vue 的结合使用说明
1.第一步有一个Django项目 先是创建一个Django项目 django-admin startproject demo 然后创建一个application应用 python manage.py ...
- Android P的APP适配总结,让你快人一步
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由QQ音乐技术团队发表于云+社区专栏 上篇:Android P 行为变更适配 Android P 这次有很多行为变更,其中不乏一些需要亟 ...
- java的优点和误解 《java核心技术卷i》第一章
<java核心技术卷i>第一章主要内容包括三点: 1:Java白皮书的关键术语:描述Java的十一个关键字: 2:Java applet 3 :关于Java的常见误解 1:第一章:Ja ...
- springboot 学习之路 6(集成durid连接池)
目录:[持续更新.....] spring 部分常用注解 spring boot 学习之路1(简单入门) spring boot 学习之路2(注解介绍) spring boot 学习之路3( 集成my ...
- 修改minifest使桌面软件支持高dpi
在VisualStudio中可以很方便的设置manifest以支持高dpi的用户界面.当然也可以手工修改manifest文件来添加对高dpi的支持. QQ在高dpi方面做的尤其差,对高dpi的支持迟迟 ...
- int,int32_t,int64_t
一.数据类型特别是int相关的类型在不同位数机器的平台下长度不同.C99标准并不规定具体数据类型的长度大小,只规定级别.作下比较: 16位平台 char 1个字节8位short ...
- CentOS6.5内 MySQL5.7.19编译安装
作为博主这样的Linux菜鸟,CentOS下最喜欢的就是yum安装.但有时候因为特殊情况(例如被墙等),某些软件可能没办法直接通过yum来安装,这时候我们可以使用编译安装或者直接二进制文件安装. 本博 ...
- Python对文件的解压和压缩
zipfile: 解压: import os, zipfile serverzip_path = 'D:\\server.zip' serverzip_target_path = 'd:\\2' f ...
- 阿里云ECS Ubuntu16.0 安装 uwsgi 失败解决方案
Ubuntu安装包时报错 E:Unable to locate package xxx(如:python3-pip) 一般新安装Ubuntu后需要先更新软件源: apt-get update apt- ...