多线程的实现方式有实现Runnable接口和继承Thread类(实际上Thread类也实现了Runnable接口),但是Runnable接口的方式有两个弊端,第一个是不能获取返回结果,第二个是不能抛出exception。但是Callable接口很好的解决了上面的问题。下面介绍Callable接口的使用方法。

  

0.我们先看JDKAPI对callable接口的解释:

  1. public interface Callable<V>
  1.  

  返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。

  Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。

  Executors 类包含一些从其他普通形式转换成 Callable 类的实用方法。

方法解释:

call

  1. V call()
  2. throws Exception
计算结果,如果无法计算结果,则抛出一个异常。

返回:
计算的结果
抛出:
Exception - 如果无法计算结果

1.第一个实现Callable接口开启线程的用法:(不接受返回值,只是开启线程执行任务)

  1. package threadTest;
  2.  
  3. import java.util.concurrent.Callable;
  4. import java.util.concurrent.FutureTask;
  5.  
  6. /**
  7. * 实现callable接口,实现Callable接口
  8. *
  9. *
  10. */
  11. public class MyCallable implements Callable<String> {
  12.  
  13. /**
  14. * 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
  15. */
  16. @Override
  17. public String call() {
  18. System.out.println(Thread.currentThread().getName() + " 执行callable的call方法");
  19. return "result";
  20. }
  21.  
  22. public static void main(String[] args) {
  23. // 1.创建callable对象
  24. Callable<String> myCallable = new MyCallable();
  25. // 2.由上面的callable对象创建一个FutureTask对象
  26. FutureTask<String> oneTask = new FutureTask<String>(myCallable);
  27. // 3.由FutureTask创建一个Thread对象
  28. Thread t = new Thread(oneTask);
  29. // 4.开启线程
  30. t.start();
  31. }
  32.  
  33. }

结果:

  1. Thread-0 执行callablecall方法

解释:

  (1)Callable接口不用解释了,就是一个类似于Runnable接口的接口,只有一个call方法,此方法有返回值,可以抛出异常。

  (2)Futuretask类查看:实现了RunnableFuture接口,RunnableFuture接口继承于Runnable接口和Future接口:所以Futuretask是Runnable的对象(子类的对象也是父类的对象)

  1. public class FutureTask<V> implements RunnableFuture<V> {

查看此类的构造方法:初始化成员变量callable

  1. public FutureTask(Callable<V> callable) {
  2. if (callable == null)
  3. throw new NullPointerException();
  4. this.callable = callable;
  5. this.state = NEW; // ensure visibility of callable
  6. }

查看此类的run方法:(调用Callable的call()方法)

  1. public void run() {
  2. if (state != NEW ||
  3. !UNSAFE.compareAndSwapObject(this, runnerOffset,
  4. null, Thread.currentThread()))
  5. return;
  6. try {
  7. Callable<V> c = callable;
  8. if (c != null && state == NEW) {
  9. V result;
  10. boolean ran;
  11. try {
  12. result = c.call();
  13. ran = true;
  14. } catch (Throwable ex) {
  15. result = null;
  16. ran = false;
  17. setException(ex);
  18. }
  19. if (ran)
  20. set(result);
  21. }
  22. } finally {
  23. // runner must be non-null until state is settled to
  24. // prevent concurrent calls to run()
  25. runner = null;
  26. // state must be re-read after nulling runner to prevent
  27. // leaked interrupts
  28. int s = state;
  29. if (s >= INTERRUPTING)
  30. handlePossibleCancellationInterrupt(s);
  31. }
  32. }

    JDKAPI对此类的解释:

    

      

  (3)RunnableFuture接口:

  1. public interface RunnableFuture<V> extends Runnable, Future<V> {
  2. void run();
  3. }

  (4)Runnable接口不用解释了,Future接口的解释如下:===也就是Future用于获取Callable执行的返回结果

  1. package java.util.concurrent;
  2. public interface Future<V> {
  3.  
  4. boolean cancel(boolean mayInterruptIfRunning);
  5.  
  6. boolean isCancelled();
  7.  
  8. boolean isDone();
  9.  
  10. V get() throws InterruptedException, ExecutionException;
  11.  
  12. V get(long timeout, TimeUnit unit)
  13. throws InterruptedException, ExecutionException, TimeoutException;
  14. }

  JDKAPI的解释:

  1. public interface Future<V>

  Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。

用法示例(注意,下列各类都是构造好的。)

  1. interface ArchiveSearcher { String search(String target); }
  2. class App {
  3. ExecutorService executor = ...
  4. ArchiveSearcher searcher = ...
  5. void showSearch(final String target)
  6. throws InterruptedException {
  7. Future<String> future
  8. = executor.submit(new Callable<String>() {
  9. public String call() {
  10. return searcher.search(target);
  11. }});
  12. displayOtherThings(); // do other things while searching
  13. try {
  14. displayText(future.get()); // use future
  15. } catch (ExecutionException ex) { cleanup(); return; }
  16. }
  17. }

FutureTask 类是 Future 的一个实现,Future 可实现 Runnable,所以可通过 Executor 来执行。例如,可用下列内容替换上面带有 submit 的构造:

  1. FutureTask<String> future =
  2. new FutureTask<String>(new Callable<String>() {
  3. public String call() {
  4. return searcher.search(target);
  5. }});
  6. executor.execute(future);

  (5)Thread类可以接受一个Runnable接口,上面代码FutureTask实现了RunnableFuture接口,RunnableFuture接口继承于Runnable接口,所以可以接受FutureTask对象。

2.使用ExecutorService、Callable、Future实现有返回结果的线程

 1.单线程的获取返回结果:

  1. package threadTest;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import java.util.concurrent.Callable;
  6. import java.util.concurrent.ExecutionException;
  7. import java.util.concurrent.ExecutorService;
  8. import java.util.concurrent.Executors;
  9. import java.util.concurrent.Future;
  10.  
  11. /**
  12. * 实现callable接口,实现Callable接口
  13. *
  14. *
  15. */
  16. public class MyCallable implements Callable<String> {
  17.  
  18. /**
  19. * 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
  20. */
  21. @Override
  22. public String call() {
  23. System.out.println(Thread.currentThread().getName() + " 执行callable的call方法");
  24. return "result";
  25. }
  26.  
  27. public static void main(String[] args) {
  28. test1();
  29. }
  30.  
  31. /**
  32. * 单个线程
  33. */
  34. public static void test1() {
  35. // 1.创建固定大小的线程池
  36. ExecutorService es = Executors.newFixedThreadPool(1);
  37. // 2.提交线程任务,用Future接口接受返回的实现类
  38. Future<String> future = es.submit(new MyCallable());
  39. // 3.关闭线程池
  40. es.shutdown();
  41. // 4.调用future.get()获取callable执行完成的返回结果
  42. String result;
  43. try {
  44. result = future.get();
  45. System.out.println(Thread.currentThread().getName() + "\t" + result);
  46. } catch (InterruptedException | ExecutionException e) {
  47. e.printStackTrace();
  48. }
  49. }
  50. }

结果:

pool-1-thread-1 执行callable的call方法
main result

2.多个线程的执行返回结果:

  1. package threadTest;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import java.util.concurrent.Callable;
  6. import java.util.concurrent.ExecutionException;
  7. import java.util.concurrent.ExecutorService;
  8. import java.util.concurrent.Executors;
  9. import java.util.concurrent.Future;
  10.  
  11. /**
  12. * 实现callable接口,实现Callable接口
  13. *
  14. *
  15. */
  16. public class MyCallable implements Callable<String> {
  17.  
  18. /**
  19. * 实现call方法,接口中抛出异常。因为子类不可以比父类干更多的坏事,所以子类可以不抛出异常
  20. */
  21. @Override
  22. public String call() {
  23. System.out.println(Thread.currentThread().getName() + " 执行callable的call方法");
  24. return "result";
  25. }
  26.  
  27. public static void main(String[] args) {
  28. test2();
  29. }
  30. /**
  31. * 多个线程
  32. */
  33. public static void test2() {
  34. // 1.创建固定大小的线程池(5个)
  35. int threadNum = 5;
  36. ExecutorService es = Executors.newFixedThreadPool(threadNum);
  37. // 2.提交线程任务,用Future接口接受返回的实现类
  38. List<Future<String>> futures = new ArrayList<Future<String>>(threadNum);
  39. for (int i = 0; i < threadNum; i++) {
  40. Future<String> future = es.submit(new MyCallable());
  41. futures.add(future);
  42. }
  43. // 3.关闭线程池
  44. es.shutdown();
  45. // 4.调用future.get()获取callable执行完成的返回结果
  46. for (Future<String> future : futures) {
  47. try {
  48. String result = future.get();
  49. System.out.println(Thread.currentThread().getName() + "\t" + result);
  50. } catch (InterruptedException | ExecutionException e) {
  51. e.printStackTrace();
  52. }
  53. }
  54. }
  55.  
  56. }

结果:

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. C# 数据库批量插入数据之 —— SqlBulkCopy、表值参数

    创建了一个用来测试的Student表: CREATE TABLE [dbo].[Student]( [ID] [int] PRIMARY KEY NOT NULL, ) NULL, ) NULL, [ ...

  3. Redis分布式锁----悲观锁实现,以秒杀系统为例

    摘要:本文要实现的是一种使用redis来实现分布式锁. 1.分布式锁 分布式锁在是一种用来安全访问分式式机器上变量的安全方案,一般用在全局id生成,秒杀系统,全局变量共享.分布式事务等.一般会有两种实 ...

  4. CentOS7 yum安装、配置PostgreSQL 9.5

    PostgreSQL 9.5安装 1.添加RPM yum install https://download.postgresql.org/pub/repos/yum/9.5/redhat/rhel-7 ...

  5. Xstart Insatll And Usage

    不进入 Linux 桌面环境,又需要运行一些图形化的软件,比如 Oracle 数据库的安装等 安装 Windows 上 Xstart 安装:https://www.cnblogs.com/jhxxb/ ...

  6. CentOS7 下 Hadoop 单节点(伪分布式)部署

    Hadoop 下载 (2.9.2) https://hadoop.apache.org/releases.html 准备工作 关闭防火墙 (也可放行) # 停止防火墙 systemctl stop f ...

  7. Mac下显示网页全屏快捷键

    control+command+F mac下谷歌浏览器全屏时隐藏头部:(隐藏标签页和地址栏) command+shift+B

  8. canvas绘图history妙用

    function palette(canvas,ctx){ //初始化画布内部元素默认样式 this.strokeColor = 'red'; //默认选中红色触发颜色 this.fillColor ...

  9. Spark源码剖析 - SparkContext的初始化(九)_启动测量系统MetricsSystem

    9. 启动测量系统MetricsSystem MetricsSystem使用codahale提供的第三方测量仓库Metrics.MetricsSystem中有三个概念: Instance:指定了谁在使 ...

  10. windows 使用 php 的exif 问题 Call to undefined function exif_imagetype()

    保证 extension=php_mbstring.dll 在 extension=php_exif.dll 之前