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中。

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

from: https://www.cnblogs.com/felixzh/p/6044371.html

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

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

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

  2. Java Callable接口与Future接口的两种使用方式

    Java Callable.Future的两种使用方式Callable+Futurepublic class Test { public static void main(String[] args) ...

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

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

  4. java Future 接口介绍

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

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

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

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

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

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

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

  8. 浅谈Java Future接口

    Java项目编程中,为了充分利用计算机CPU资源,一般开启多个线程来执行异步任务.但不管是继承Thread类还是实现Runnable接口,都无法获取任务执行的结果.JDK 5中引入了Callable和 ...

  9. Java程序执行超时——Future接口介绍

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

随机推荐

  1. 【LOJ】#2551. 「JSOI2018」列队

    题解 老年选手一道裸的主席树都要看好久才看出来 首先熟练的把这个区间建成\(n\)个主席树 然后对于一个询问,我们相当于在主席树上二分一个mid,使得\(mid - K + 1\)正好和\([l,r] ...

  2. 上海高校金马五校赛 J - 小Y写文章

    题目大意: 给你n个数字, 定义不连贯值为, max(abs(a[ i ] - b[ i ])) ,现在让你把m个新的数字插入n + 1 个空位中,使得不连贯值最小. 思路:二分不连贯值, 每次进行二 ...

  3. 多线程编程中的EventWaitHandler

    首先如果读者是.Net多线程编程的老手,就不用看这篇文章了,这篇文章主要是阐述EventWaitHandler的一些基本原理和用法. 在.NET的System.Threading命名空间中有一个名叫W ...

  4. (3)python tkinter-消息框、对话框、文件对话框

    消息框 导入 import tkinter import tkinter.messagebox #这个是消息框,对话框的关键 提示消息框 tkinter.messagebox.showinfo('提示 ...

  5. cookie之困

    参见http://yun.baidu.com/share/link?shareid=1575530779&uk=1795493794 cookie三元组(name,domain,path),它 ...

  6. Qt5编译oracle驱动教程

    我们都知道oracle数据库的强大,并且好多企业或者教学用到数据库时都会推荐使用.但是Qt因为版权问题没有封装oracle数据库专用驱动,网上也有一大堆说法和教程,但是或多或少的都有问题.下面废话不多 ...

  7. html (第四本书第四章参考)

    上机1 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8 ...

  8. eclipse launching workspace太慢的解决方法

    这几天eclipse调试Android项目的时候反应超慢,右下显示launching workspace就不怎么动了,今天终于卡的受不了了,在网上搜了写方法,设置了下总算好点了,现在把方法贴出来,跟大 ...

  9. 1200 同余方程 2012年NOIP全国联赛提高组

    题目描述 Description 求关于 x 同余方程 ax ≡ 1 (mod b)的最小正整数解. 输入描述 Input Description 输入只有一行,包含两个正整数 a, b,用 一个 空 ...

  10. Intel Code Challenge Elimination Round (Div.1 + Div.2, combined) B. Verse Pattern 水题

    B. Verse Pattern 题目连接: http://codeforces.com/contest/722/problem/B Description You are given a text ...