多线程的实现方式有实现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获取返回值的更多相关文章

  1. java线程(1)——三种创建线程的方式

    前言 线程,英文Thread.在java中,创建线程的方式有三种: 1.Thread 2.Runnable 3.Callable 在详细介绍下这几种方式之前,我们先来看下Thread类和Runnabl ...

  2. iOS用三种途径实现一方法有多个返回值

    以前觉得这种标题有点偏向于理论,实际开发中怎么会有这种诡异的需求,但是真正遇到了这种硬需求时觉得还是有那么点价值的,理论付诸了实践在此也就做了个整理. 以我私下开发中的一处代码为例,本意是希望有这么一 ...

  3. java 实现md5加密的三种方式与解密

      java 实现md5加密的三种方式 CreateTime--2018年5月31日15点04分 Author:Marydon 一.解密 说明:截止文章发布,Java没有实现解密,但是已有网站可以免费 ...

  4. Java连接Oracle数据库的三种连接方式

    背景: 这两天在学习Oracle数据库,这里就总结下自己上课所学的知识,同时记录下来,方便整理当天所学下的知识,也同时方便日后自己查询. SQL语句的话,这里我就不多讲了,感觉和其他的数据库(MySQ ...

  5. java加载jdbc驱动三种方式的比较

    一.引言 平时连接数据库的时候首先要加载jdbc驱动,这一步骤其实有三种方式,他们的区别?优劣? 二.快速了解三种加载方式 Class.forName(“com.mysql.jdbc.Driver”) ...

  6. java 创建线程的三种方法Callable,Runnable,Thread比较及用法

    转自:http://www.chinaitlab.com/Java/line/942440.html 编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互.一般有三种方法,Thread, ...

  7. 框架源码系列九:依赖注入DI、三种Bean配置方式的注册和实例化过程

    一.依赖注入DI 学习目标1)搞清楚构造参数依赖注入的过程及类2)搞清楚注解方式的属性依赖注入在哪里完成的.学习思路1)思考我们手写时是如何做的2)读 spring 源码对比看它的实现3)Spring ...

  8. 深入浅出spring IOC中三种依赖注入方式

    深入浅出spring IOC中三种依赖注入方式 spring的核心思想是IOC和AOP,IOC-控制反转,是一个重要的面向对象编程的法则来消减计算机程序的耦合问题,控制反转一般分为两种类型,依赖注入和 ...

  9. javascript中var let const三种变量声明方式

    javascript中var let const三种变量声明方式 1.var  ①var表示声明了一个变量,并且可以同时初始化该变量. ②使用var语句声明的变量的作用域是当前执行位置的上下文:一个函 ...

随机推荐

  1. Vue(基础六)_嵌套路由(续)

    一.前言                  1.路由嵌套里面的公共路由                  2.keep-alive路由缓存                  3.导航守卫 二.主要内容 ...

  2. 解决Nginx出现403 forbidden (13: Permission denied)报错的四种方法

    我是在在本地用虚拟机中通过yum安装nginx的,安装一切正常,但是访问时报403, 于是查看nginx日志,路径为/var/log/nginx/error.log.打开日志发现报错Permissio ...

  3. 黑苹果是否会成为mac电脑的竞争对手?

    最近黑科技技术大佬们都在传扬windows系统上安装mac系统,不用买mac电脑也可以使用mac一样的操作环境.什么是黑苹果?一起来看看如何在windows系统上安装mac系统. 更多专题,可关注小编 ...

  4. 干货分享:互联网运营 学习SEO从零开始 SEO深度解析学习笔记

    最近在自学SEO,互联网运营,把做的笔记干货分享给大家啊! 希望能帮到大家,如有好的建议可以关注我[磨人的小妖精]或留言,大家一起探讨. 之前还写过一篇文章互联网运营+SEO:推荐必看的5本书籍,学习 ...

  5. redis注册成window服务 标签: redis

    注册服务 redis-server.exe –service-install redis.windows.conf 删除服务 redis-server –service-uninstall 开启服务 ...

  6. STM32学习笔记:【001】常见数据查阅

    了方便开发学习,现整理在学习过程中经常查阅的资料 (注意,以下资料都可以在ST给出官方手册中查到) 本人所持型号:STM32F429ZI-DISCOVERY, 芯片内核       :Cortex - ...

  7. HDU - 6311 Cover(无向图的最少路径边覆盖 欧拉路径)

    题意 给个无向图,无重边和自环,问最少需要多少路径把边覆盖了.并输出相应路径 分析 首先联通块之间是独立的,对于一个联通块内,最少路径覆盖就是  max(1,度数为奇数点的个数/2).然后就是求欧拉路 ...

  8. Shell编程(二)Bash中调用Python

  9. springBoot中的定时任务

    springBoot中的定时任务 1:在Spring Boot的主类中加入@EnableScheduling注解,启用定时任务的配置 2:新建ScheduledTasks任务类 : package c ...

  10. 错误 1 未能找到类型或命名空间名称“DataPager”(是否缺少 using 指令或程序集引用?)

    鄙人在设计器SearchTab.xaml中添加了如下一个分页控件: <sdk:DataPager x:Name="dataPagerPrj" Grid.Row="3 ...