多线程的实现方式有实现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. C++ vector的运用

    C++ vector 用法(#include <vector>) 标准库Vector类型 使用需要的头文件: #include <vector> Vector:Vector 是 ...

  2. bzoj3718 树状数组

    https://www.lydsy.com/JudgeOnline/problem.php?id=3718 有时候,要透过题面看到本质 题意 你的老板命令你将停车场里的车移动成他想要的样子.停车场是一 ...

  3. go官方的http.request + context样例

    go官方的http.request + context样例 https://github.com/DavadDi/go_study/blob/master/src/httpreq_context/ma ...

  4. python德国信用评分卡建模(附代码AAA推荐)

    欢迎关注博主主页,学习python视频资源,还有大量免费python经典文章 python信用评分卡建模视频系列教程(附代码)  博主录制 https://study.163.com/course/i ...

  5. MAC上截图,编辑图片与恢复图片

    1.Command+shift+3:全屏截图,保存截图到桌面 2.Command+shift+4:鼠标选定区域截图,保存截图到桌面 3.Command+shift+4+Space(空格键):程序窗口截 ...

  6. Kafka技术内幕 读书笔记之(五) 协调者——消费组状态机

    协调者保存的消费组元数据中记录了消费组的状态机 , 消费组状态机的转换主要发生在“加入组请求”和“同步组请求”的处理过程中 .协调者处理“离开消费组请求”“迁移消费组请求”“心跳请求” “提交偏移量请 ...

  7. Git(工作区和暂存区概念)

    Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念. 先来看名词解释. 工作区(Working Directory) 就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工 ...

  8. getResource()和getResourceAsStream以及路径问题【转】【补】

    一 getResource 用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要取得c:/test.txt文件,就 ...

  9. HTML第二耍 列表标签

    先复习下上一节 <!doctype html> <html> <head> <meta charset="utf-8"> <t ...

  10. 细说REST API安全之防止重放攻击

    一. 重放攻击概述 百科对重放攻击的描述:https://zh.wikipedia.org/wiki/%E9%87%8D%E6%94%BE%E6%94%BB%E5%87%BB简而言之,重放攻击的产生是 ...