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
- 计算结果,如果无法计算结果,则抛出一个异常。
-
- 返回:
- 计算的结果
- 抛出:
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语句声明的变量的作用域是当前执行位置的上下文:一个函 ...
随机推荐
- Vue(基础六)_嵌套路由(续)
一.前言 1.路由嵌套里面的公共路由 2.keep-alive路由缓存 3.导航守卫 二.主要内容 ...
- C# 数据库批量插入数据之 —— SqlBulkCopy、表值参数
创建了一个用来测试的Student表: CREATE TABLE [dbo].[Student]( [ID] [int] PRIMARY KEY NOT NULL, ) NULL, ) NULL, [ ...
- Redis分布式锁----悲观锁实现,以秒杀系统为例
摘要:本文要实现的是一种使用redis来实现分布式锁. 1.分布式锁 分布式锁在是一种用来安全访问分式式机器上变量的安全方案,一般用在全局id生成,秒杀系统,全局变量共享.分布式事务等.一般会有两种实 ...
- CentOS7 yum安装、配置PostgreSQL 9.5
PostgreSQL 9.5安装 1.添加RPM yum install https://download.postgresql.org/pub/repos/yum/9.5/redhat/rhel-7 ...
- Xstart Insatll And Usage
不进入 Linux 桌面环境,又需要运行一些图形化的软件,比如 Oracle 数据库的安装等 安装 Windows 上 Xstart 安装:https://www.cnblogs.com/jhxxb/ ...
- CentOS7 下 Hadoop 单节点(伪分布式)部署
Hadoop 下载 (2.9.2) https://hadoop.apache.org/releases.html 准备工作 关闭防火墙 (也可放行) # 停止防火墙 systemctl stop f ...
- Mac下显示网页全屏快捷键
control+command+F mac下谷歌浏览器全屏时隐藏头部:(隐藏标签页和地址栏) command+shift+B
- canvas绘图history妙用
function palette(canvas,ctx){ //初始化画布内部元素默认样式 this.strokeColor = 'red'; //默认选中红色触发颜色 this.fillColor ...
- Spark源码剖析 - SparkContext的初始化(九)_启动测量系统MetricsSystem
9. 启动测量系统MetricsSystem MetricsSystem使用codahale提供的第三方测量仓库Metrics.MetricsSystem中有三个概念: Instance:指定了谁在使 ...
- windows 使用 php 的exif 问题 Call to undefined function exif_imagetype()
保证 extension=php_mbstring.dll 在 extension=php_exif.dll 之前