Java线程实现的第三种方式Callable方式与结合Future获取返回值
多线程的实现方式有实现Runnable接口和继承Thread类(实际上Thread类也实现了Runnable接口),但是Runnable接口的方式有两个弊端,第一个是不能获取返回结果,第二个是不能抛出exception。但是Callable接口很好的解决了上面的问题。下面介绍Callable接口的使用方法。
0.我们先看JDKAPI对callable接口的解释:
public interface Callable<V>
返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。
Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
Executors 类包含一些从其他普通形式转换成 Callable 类的实用方法。
方法解释:
call
V call()
throws Exception
- 计算结果,如果无法计算结果,则抛出一个异常。
-
- 返回:
- 计算的结果
- 抛出:
Exception- 如果无法计算结果
1.第一个实现Callable接口开启线程的用法:(不接受返回值,只是开启线程执行任务)
package threadTest; import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask; /**
* 实现callable接口,实现Callable接口
*
*
*/
public class MyCallable implements Callable<String> { /**
* 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
*/
@Override
public String call() {
System.out.println(Thread.currentThread().getName() + " 执行callable的call方法");
return "result";
} public static void main(String[] args) {
// 1.创建callable对象
Callable<String> myCallable = new MyCallable();
// 2.由上面的callable对象创建一个FutureTask对象
FutureTask<String> oneTask = new FutureTask<String>(myCallable);
// 3.由FutureTask创建一个Thread对象
Thread t = new Thread(oneTask);
// 4.开启线程
t.start();
} }
结果:
Thread-0 执行callable的call方法
解释:
(1)Callable接口不用解释了,就是一个类似于Runnable接口的接口,只有一个call方法,此方法有返回值,可以抛出异常。
(2)Futuretask类查看:实现了RunnableFuture接口,RunnableFuture接口继承于Runnable接口和Future接口:所以Futuretask是Runnable的对象(子类的对象也是父类的对象)
public class FutureTask<V> implements RunnableFuture<V> {
查看此类的构造方法:初始化成员变量callable
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
查看此类的run方法:(调用Callable的call()方法)
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
JDKAPI对此类的解释:

(3)RunnableFuture接口:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
(4)Runnable接口不用解释了,Future接口的解释如下:===也就是Future用于获取Callable执行的返回结果
package java.util.concurrent;
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
JDKAPI的解释:
public interface Future<V>
Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。
用法示例(注意,下列各类都是构造好的。)
interface ArchiveSearcher { String search(String target); }
class App {
ExecutorService executor = ...
ArchiveSearcher searcher = ...
void showSearch(final String target)
throws InterruptedException {
Future<String> future
= executor.submit(new Callable<String>() {
public String call() {
return searcher.search(target);
}});
displayOtherThings(); // do other things while searching
try {
displayText(future.get()); // use future
} catch (ExecutionException ex) { cleanup(); return; }
}
}
FutureTask 类是 Future 的一个实现,Future 可实现 Runnable,所以可通过 Executor 来执行。例如,可用下列内容替换上面带有 submit 的构造:
FutureTask<String> future =
new FutureTask<String>(new Callable<String>() {
public String call() {
return searcher.search(target);
}});
executor.execute(future);
(5)Thread类可以接受一个Runnable接口,上面代码FutureTask实现了RunnableFuture接口,RunnableFuture接口继承于Runnable接口,所以可以接受FutureTask对象。
2.使用ExecutorService、Callable、Future实现有返回结果的线程
1.单线程的获取返回结果:
package threadTest; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; /**
* 实现callable接口,实现Callable接口
*
*
*/
public class MyCallable implements Callable<String> { /**
* 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
*/
@Override
public String call() {
System.out.println(Thread.currentThread().getName() + " 执行callable的call方法");
return "result";
} public static void main(String[] args) {
test1();
} /**
* 单个线程
*/
public static void test1() {
// 1.创建固定大小的线程池
ExecutorService es = Executors.newFixedThreadPool(1);
// 2.提交线程任务,用Future接口接受返回的实现类
Future<String> future = es.submit(new MyCallable());
// 3.关闭线程池
es.shutdown();
// 4.调用future.get()获取callable执行完成的返回结果
String result;
try {
result = future.get();
System.out.println(Thread.currentThread().getName() + "\t" + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
结果:
pool-1-thread-1 执行callable的call方法
main result
2.多个线程的执行返回结果:
package threadTest; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; /**
* 实现callable接口,实现Callable接口
*
*
*/
public class MyCallable implements Callable<String> { /**
* 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
*/
@Override
public String call() {
System.out.println(Thread.currentThread().getName() + " 执行callable的call方法");
return "result";
} public static void main(String[] args) {
test2();
}
/**
* 多个线程
*/
public static void test2() {
// 1.创建固定大小的线程池(5个)
int threadNum = 5;
ExecutorService es = Executors.newFixedThreadPool(threadNum);
// 2.提交线程任务,用Future接口接受返回的实现类
List<Future<String>> futures = new ArrayList<Future<String>>(threadNum);
for (int i = 0; i < threadNum; i++) {
Future<String> future = es.submit(new MyCallable());
futures.add(future);
}
// 3.关闭线程池
es.shutdown();
// 4.调用future.get()获取callable执行完成的返回结果
for (Future<String> future : futures) {
try {
String result = future.get();
System.out.println(Thread.currentThread().getName() + "\t" + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
} }
结果:
pool-1-thread-1 执行callable的call方法
pool-1-thread-2 执行callable的call方法
pool-1-thread-4 执行callable的call方法
pool-1-thread-3 执行callable的call方法
main result
pool-1-thread-5 执行callable的call方法
main result
main result
main result
main result
总结:
ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
线程池的使用参考:https://www.cnblogs.com/qlqwjy/p/9470414.html
Java线程实现的第三种方式Callable方式与结合Future获取返回值的更多相关文章
- java线程(1)——三种创建线程的方式
前言 线程,英文Thread.在java中,创建线程的方式有三种: 1.Thread 2.Runnable 3.Callable 在详细介绍下这几种方式之前,我们先来看下Thread类和Runnabl ...
- iOS用三种途径实现一方法有多个返回值
以前觉得这种标题有点偏向于理论,实际开发中怎么会有这种诡异的需求,但是真正遇到了这种硬需求时觉得还是有那么点价值的,理论付诸了实践在此也就做了个整理. 以我私下开发中的一处代码为例,本意是希望有这么一 ...
- java 实现md5加密的三种方式与解密
java 实现md5加密的三种方式 CreateTime--2018年5月31日15点04分 Author:Marydon 一.解密 说明:截止文章发布,Java没有实现解密,但是已有网站可以免费 ...
- Java连接Oracle数据库的三种连接方式
背景: 这两天在学习Oracle数据库,这里就总结下自己上课所学的知识,同时记录下来,方便整理当天所学下的知识,也同时方便日后自己查询. SQL语句的话,这里我就不多讲了,感觉和其他的数据库(MySQ ...
- java加载jdbc驱动三种方式的比较
一.引言 平时连接数据库的时候首先要加载jdbc驱动,这一步骤其实有三种方式,他们的区别?优劣? 二.快速了解三种加载方式 Class.forName(“com.mysql.jdbc.Driver”) ...
- java 创建线程的三种方法Callable,Runnable,Thread比较及用法
转自:http://www.chinaitlab.com/Java/line/942440.html 编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互.一般有三种方法,Thread, ...
- 框架源码系列九:依赖注入DI、三种Bean配置方式的注册和实例化过程
一.依赖注入DI 学习目标1)搞清楚构造参数依赖注入的过程及类2)搞清楚注解方式的属性依赖注入在哪里完成的.学习思路1)思考我们手写时是如何做的2)读 spring 源码对比看它的实现3)Spring ...
- 深入浅出spring IOC中三种依赖注入方式
深入浅出spring IOC中三种依赖注入方式 spring的核心思想是IOC和AOP,IOC-控制反转,是一个重要的面向对象编程的法则来消减计算机程序的耦合问题,控制反转一般分为两种类型,依赖注入和 ...
- javascript中var let const三种变量声明方式
javascript中var let const三种变量声明方式 1.var ①var表示声明了一个变量,并且可以同时初始化该变量. ②使用var语句声明的变量的作用域是当前执行位置的上下文:一个函 ...
随机推荐
- 建立Heapster Influxdb Grafana集群性能监控平台
依赖于kubenets dns服务 图形化展示度量指标的实现需要集成k8s的另外一个Addons组件: Heapster .Heapster原生支持K8s(v1.0.6及以后版本)和 CoreOS , ...
- 运维监控-Open-Falcon安装Agent实战篇
运维监控-Open-Falcon安装Agent实战篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 本文参考链接来自:http://book.open-falcon.org/zh/ ...
- Linux shell 自动删除n天前日志
linux是一个很能自动产生文件的系统,日志.邮件.备份等.虽然现在硬盘廉价,我们可以有很多硬盘空间供这些文件浪费,让系统定时清理一些不需要的文件很有一种爽快的事情.不用你去每天惦记着是否需要清理日志 ...
- java 可设置最大内存
测试方法:在命令行下用 java -XmxXXXXM -version 命令来进行测试,然后逐渐的增大XXXX的值,如果执行正常就表示指定的内存大小可用,否则会打印错误信息. 堆(Heap)和非堆(N ...
- ruby读取exce文件,使用roo---Gem
module SEquipsHelper #设备台账,从excel文件读取信息 require 'roo' #require 'roo-xls' #读取excel文件 # SEquipsHelper. ...
- Process和ProcessBuilder入门【原】
ProcessBuilder优点 ProcessBuilder(XXX).start()和Runtime.exec(XXX)功能相同,主要优点在使用过程中感受有: 前者是jdk1.5后的新方式 配置环 ...
- ZOJ - 3450 Doraemon's Railgun (dp)
https://vjudge.net/problem/ZOJ-3450 题意 一座位落(X0,Y0)的城市将遭受n个敌人的摧残.现在我们手上有某科学的超电磁炮,每次攻击都是一条射线,对于共线的敌人,必 ...
- SVN快速入门笔记【转】
1. SVN版本控制软件目的 协作开发 远程开发 版本回退 2. 什么是SVN subVersion 支持平台操作 支持版本回退 3. 获取SVN软件 属于C/S结构软件(客户端与服务端) serve ...
- MVC实用架构设计(三)——EF-Code First(1):Repository,UnitOfWork,DbContext
前言 终于到EF了,实在不好意思,最近有点忙,本篇离上一篇发布已经一个多星期了,工作中的小迭代告一段落,终于有点时间来继续我们的架构设计了,在这里先对大家表示歉意. 其实这段时间我并不是把这个系列给忘 ...
- 细说shiro之四:在web应用中使用shiro
官网:https://shiro.apache.org/ 1. 下载在Maven项目中的依赖配置如下: <!-- shiro配置 --> <dependency> <gr ...