1. Callable与Runable区别

Java从发布的第一个版本开始就可以很方便地编写多线程的应用程序,并在设计中引入异步处理。Thread类、Runnable接口和Java内存管理模型使得多线程编程简单直接。

但Thread类和Runnable接口都不允许声明检查型异常,也不能定义返回值。没有返回值这点稍微有点麻烦。不能声明抛出检查型异常则更麻烦一些。

public void run()方法契约意味着你必须捕获并处理检查型异常。即使你小心地保存了异常信息(在捕获异常时)以便稍后检查,但也不能保证这个类(Runnable对象)的所有使用者都读取异常信息。

你也可以修改Runnable实现的getter,让它们都能抛出任务执行中的异常。但这种方法除了繁琐也不是十分安全可靠,你不能强迫使用者调用这些方法,程序员很可能会调用join()方法等待线程结束然后就不管了。

但是现在不用担心了,以上的问题终于在1.5中解决了。Callable接口和Future接口的引入以及他们对线程池的支持优雅地解决了这两个问题。

不管用哪种方式创建线程,其本质都是Callable接口与Runable接口。两者都是可被其它线程执行的任务!!区别是:

(1)Callable规定的方法是call(),而Runnable规定的方法是run()。

(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。

(3)call()方法可抛出异常,而run()方法是不能抛出异常的。

(4)运行Callable任务可拿到一个Future对象。

2.Future

如上所说,Callable任务返回Future对象。即:Callable和Future一个产生结果,一个拿到结果。

Future 表示异步计算的结果。Future接口中有如下方法:

  • boolean cancel(boolean mayInterruptIfRunning)

取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束

  • boolean isCancelled()

任务是否已经取消,任务正常完成前将其取消,则返回 true

  • boolean isDone()

任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true

  • get()

等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException

  • get(long timeout, TimeUnit unit)

同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException

Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果。也可以设置任务执行的超时时间,这个设置超时的方法就是实现Java程序执行超时的关键。

所以,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现。

三个简单的小例子,体会一下:

package com.zyf.Future;

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;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; public class FutureGetTimeOut1 {
public static void main(String[] args){
int timeout = 2;
ExecutorService executor = Executors.newSingleThreadExecutor();
Boolean result = false;
Future<Boolean> future = executor.submit(new TaskThread("发送请求"));//将任务提交给线程池
try {
result = future.get(timeout, TimeUnit.SECONDS);
// result = future.get(timeout, TimeUnit.MILLISECONDS); //1
System.out.println("发送请求任务的返回结果: "+result); //2
} catch (InterruptedException e) {
System.out.println("线程中断出错。");
future.cancel(true);// 中断执行此任务的线程
} catch (ExecutionException e) {
System.out.println("线程服务出错。");
future.cancel(true);
} catch (TimeoutException e) {// 超时异常
System.out.println("超时。");
future.cancel(true);
}finally{
System.out.println("线程服务关闭。");
executor.shutdown();
}
} static class TaskThread implements Callable<Boolean> {
private String t;
public TaskThread(String temp){
this.t= temp;
}
public Boolean call() {
//for用于模拟超时
for(int i=0;i<999999999;i++){
if(i==999999998){
System.out.println(t+"成功!");
}
if (Thread.interrupted()){ //很重要
return false;
}
}
System.out.println("继续执行..........");
return true;
}
}
}
package com.zyf.Future;

import java.util.concurrent.*;

public class FutureGetTimeOut2 {

    public static void main(String[] args) {
final ExecutorService service = Executors.newFixedThreadPool(1);
TaskThread taskThread = new TaskThread();
System.out.println("提交任务...begin");
Future<Object> taskFuture = service.submit(taskThread);
System.out.println("提交任务...end");
try {
Object re = taskFuture.get(60000, TimeUnit.MILLISECONDS);// 超时设置
System.out.println(re);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
System.out.println("超时 取消任务");
taskFuture.cancel(true);
System.out.println("超时 取消任务OK");
} finally {
service.shutdown();
}
}
} class TaskThread implements Callable<Object> {
public Object call() throws Exception {
String result = "空结果";
try {
System.out.println("任务开始....");
//修改sleep 的值测试超时
Thread.sleep(500);
result = "正确结果";
System.out.println("任务结束....");
} catch (Exception e) {
System.out.println("Task is interrupted!");
}
return result;
}
}
package com.zyf.Future;

import java.util.concurrent.*;

class MyCallable implements Callable<Object> {

    private int flag = 0;

    public MyCallable(int flag) {

        this.flag = flag;

    }

    public String call() throws Exception {

        if (this.flag == 0) {

            return "flag = 0";

        }

        if (this.flag == 1) {

            try {

                while (true) {

                    System.out.println("looping.");

                    Thread.sleep(2000);

                }

            } catch (InterruptedException e) {

                System.out.println("Interrupted");

            }

            return "false";

        } else {

            throw new Exception("Bad flag value!");

        }

    }

}

public class FutureGetBlock {

    public static void main(String[] args) {

        // 定义3个Callable类型的任务

        MyCallable task1 = new MyCallable(0);

        MyCallable task2 = new MyCallable(1);

        MyCallable task3 = new MyCallable(2);

        // 创建一个执行任务的服务

        ExecutorService es = Executors.newFixedThreadPool(3);

        try {

            // 提交并执行任务,任务启动时返回了一个Future对象,

            // 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作

            Future<?> future1 = es.submit(task1);

            // 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行

            System.out.println("task1: " + future1.get());

            Future<?> future2 = es.submit(task2);

            // 等待5秒后,再停止第二个任务。因为第二个任务进行的是无限循环

            Thread.sleep(5000);

            System.out.println("task2 cancel: " + future2.cancel(true));

            // 获取第三个任务的输出,因为执行第三个任务会引起异常

            // 所以下面的语句将引起异常的抛出

            Future<?> future3 = es.submit(task3);

            System.out.println("task3: " + future3.get());

        } catch (Exception e) {

            System.out.println(e.toString());

        }

        // 停止任务执行服务

        es.shutdownNow();

    }

}

3. Future实现类

3.1 FutureTask

FutureTask是一个RunnableFuture<V>,而RunnableFuture实现了Runnbale又实现了Futrue<V>这两个接口,

public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}

另外它还可以包装Runnable和Callable<V>

public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
} public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}

可以看到,Runnable会被Executors.callable()函数转换为Callable类型,即FutureTask最终都是执行Callable类型的任务。该适配函数的实现如下 :

public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
/**
* A callable that runs given task and returns given result
*/
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}

由于FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行。见下面两个例子:

package com.zyf.Future;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; public class FutureTaskDemo {
public static void main(String[] args) {
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
}; FutureTask<Integer> future = new FutureTask<Integer>(callable);
new Thread(future).start(); try {
Thread.sleep(1000);// 可能做一些事情 int result = future.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
package com.zyf.Future;

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;
import java.util.concurrent.FutureTask; public class FutureTaskDemo2 { static ExecutorService mExecutor = Executors.newSingleThreadExecutor(); public static void main(String[] args) {
futureDemo();
} static void futureDemo() {
try {
/**
* 提交runnable则没有返回值, future没有数据
*/
Future<?> future = mExecutor.submit(new Runnable() { @Override
public void run() {
fibc(20);
}
}); System.out.println("future result from runnable : " + future.get()); /**
* 提交Callable, 有返回值, future中能够获取返回值
*/
Future<Integer> result2 = mExecutor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return fibc(20);
}
}); System.out.println("future result from callable : " + result2.get()); /**
* FutureTask则是一个RunnableFuture<V>,即实现了Runnbale又实现了Futrue<V>这两个接口,
* 另外它还可以包装Runnable(实际上会转换为Callable)和Callable
* <V>,所以一般来讲是一个符合体了,它可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行
* ,并且还可以通过v get()返回执行结果,在线程体没有执行完成的时候,主线程一直阻塞等待,执行完则直接返回结果。
*/
FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return fibc(20);
}
});
// 提交futureTask
mExecutor.submit(futureTask);
System.out.println("future result from futureTask : " + futureTask.get()); mExecutor.shutdown(); } catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
} /**
* 效率底下的斐波那契数列, 耗时的操作
*
* @param num
* @return
*/
static int fibc(int num) {
if (num == 0) {
return 0;
}
if (num == 1) {
return 1;
}
return fibc(num - 1) + fibc(num - 2);
} }

如果要执行多个带返回值的任务,并取得多个返回值,两种方法:

1.先创建一个装Future类型的集合,用Executor提交的任务返回值添加到集合中,最后便利集合取出数据。

这时候,submit的task不一定是按照加入自己维护的list顺序完成的。从list中遍历的每个Future对象并不一定处于完成状态,这时调用get()方法就会被阻塞住。

如果系统是设计成每个线程完成后就能根据其结果继续做后面的事,这样对于处于list后面的但是先完成的线程就会增加了额外的等待时间。

所以jdk1.8增加了Future接口的另外一个实现类CompletionService

2.CompletionService相当于Executor加上BlockingQueue,使用场景为当子线程并发了一系列的任务以后,主线程需要实时地取回子线程任务的返回值并同时顺序地处理这些返回值,谁先返回就先处理谁。

而CompletionService的实现是维护一个保存Future对象的BlockingQueue。只有当这个Future对象状态是结束的时候,才会加入到这个Queue中,take()方法其实就是Producer-Consumer中的Consumer。它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中。

所以,先完成的必定先被取出。这样就减少了不必要的等待时间。

Callable接口、Runable接口、Future接口的更多相关文章

  1. Java Callable接口、Runable接口、Future接口

    1. Callable与Runable区别 Java从发布的第一个版本开始就可以很方便地编写多线程的应用程序,并在设计中引入异步处理.Thread类.Runnable接口和Java内存管理模型使得多线 ...

  2. Callable与Runable接口 submit与execute区别

    execute(Runnable x) 没有返回值.可以执行任务,但无法判断任务是否成功完成. submit(Runnable x) 返回一个future.可以用这个future来判断任务是否成功完成 ...

  3. Callable 和 Future接口 学习

    * Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务. * Callable和Runnable有几点不同: * (1)C ...

  4. Callable Future接口的设计原理

    我们都知道Callable接口作为任务给线程池来执行,可以通过Future对象来获取返回值,他们背后的实现原理是什么?通过总结背后的实现原理有助于我们深入的理解相关技术,做到触类旁通和举一反三. 文章 ...

  5. Future接口和Callable接口以及FeatureTask详解

    类继承关系 Callable接口 @FunctionalInterface public interface Callable<V> { V call() throws Exception ...

  6. 使用Callable和Future接口创建线程

    具体是创建Callable接口的实现类,并实现clall()方法.并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建 ...

  7. java Future 接口介绍

    (转自:http://blog.csdn.net/yangyan19870319/article/details/6093481) 在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java ...

  8. Java基础知识强化之网络编程笔记25:Android网络通信之 Future接口介绍(Java程序执行超时)

    1. Future接口简介 在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现. Future接口是Java标准API ...

  9. Java并发编程:Future接口、FutureTask类

    在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就 ...

随机推荐

  1. ios7 上 UIActivity 用的image

    在ios8 上UIActivityCategoryShare类型的UIActivity的图标支持彩色图片了,但是在ios7上不行,ios8上的 UIActivityCategoryAction类型也不 ...

  2. iOS gcd dispatch使用注意,dispatch_syn可能产生的死锁

      我们在使用dispatch_sync 时可能会出现死锁,看下面的例子: import UIKit class ViewController: UIViewController { var seri ...

  3. nyoj756_重建二叉树_先序遍历

    重建二叉树 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 题目很简单,给你一棵二叉树的后序和中序序列,求出它的前序序列(So easy!).   输入 输入有多组数 ...

  4. 【leetcode】Best Time to Buy and Sell 2(too easy)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  5. UVA 11827 Maximum GCD

    F - Maximum GCD Time Limit:1000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Given the ...

  6. August 9th 2016, Week 33rd Tuesday

    Tomorrow is never clear, our time is here. 明天是未知的,我们还是要过好当下. Tomorrow is not unpredictable, it is cl ...

  7. mysql 查看存储引擎的状态 show engine innodb status 详解

    首先,让我们来了解一下 SHOW INNODB STATUS 输出的基础,它打印了很多关于 InnoDB 内部性能相关的计数器.统计.事务处理信息等.在 MySQL 5 中,InnoDB 的性能统计结 ...

  8. chaper3_exerise_UVa455_周期串

    #include<iostream> #include<cstring> #include<stdio.h> using namespace std; ; int ...

  9. Ubuntu离线更新flashplugin

    当网络太烂时,Ubuntu更新可能会卡在下载flashplugin上面,继而出错.在U论坛上找到一篇帖子,寻到成功安装flashplugin-installer的方法: 1.首先使用sudo apt- ...

  10. VCC、VDD、VSS、 VEE 和VPP的区别

    在电子电路中,常可以看到VCC.VDD和VSS三种不同的符号,它们有什么区别呢? 一.解释 VCC:C=circuit 表示电路的意思, 即接入电路的电压: VDD:D=device 表示器件的意思, ...