Callable接口、Runable接口、Future接口

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

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

时间: 2024-08-30 12:05:30

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

Java Callable接口、Runable接口、Future接口

1. Callable与Runable区别 Java从发布的第一个版本开始就可以很方便地编写多线程的应用程序,并在设计中引入异步处理.Thread类.Runnable接口和Java内存管理模型使得多线程编程简单直接. 但Thread类和Runnable接口都不允许声明检查型异常,也不能定义返回值.没有返回值这点稍微有点麻烦.不能声明抛出检查型异常则更麻烦一些. public void run()方法契约意味着你必须捕获并处理检查型异常.即使你小心地保存了异常信息(在捕获异常时)以便稍后检查,但也

Callable与Runable接口 submit与execute区别

execute(Runnable x) 没有返回值.可以执行任务,但无法判断任务是否成功完成. submit(Runnable x) 返回一个future.可以用这个future来判断任务是否成功完成. 在Java5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类. 两者都可以被ExecutorService执行 Future future = pool.submit(new RunnableTest("Task2"));         

Java并发编程-Executor框架之Callable和Future接口

在上一篇文章中我们已经了解了Executor框架进行线程管理,这篇文章将学习Executor框架的另一个特性,我们知道执行Runnable任务是没有返回值得,但Executor可以运行并发任务并获得返回值,Concurrent包提供下面两个接口实现这个功能: Callable接口:这个接口声明call(),类似于Runnable的run(),可以在这个方法里实现任务的具体逻辑操作.Callable是一个泛型接口,必须声明call()的返回类型. Future接口:这个接口声明了一下方法来获取Ca

Callable 和 Future接口 学习

* Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务. * Callable和Runnable有几点不同: * (1)Callable规定的方法是call(),而Runnable规定的方法是run(). * (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的. * (3)call()方法可抛出异常,而run()方法是不能抛出异常的. * (4)运行Callable任务可拿到一个Futu

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

在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口. 这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果. 如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦. 而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果. 今天我们就来讨论一下Callable.Future和FutureTask三个类的使用方法.以下是本文的目录

java Future 接口介绍

(转自:http://blog.csdn.net/yangyan19870319/article/details/6093481) 在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现. Future接口是Java标准API的一部分,在java.util.concurrent包中.Future接口是Java线程Future模式的实现,可以来进行异步计算. Future模式可以这样来描述:我有一个任务,提交给了Futu

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

在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现. Future接口是Java标准API的一部分,在java.util.concurrent包中.Future接口是Java线程Future模式的实 现,可以来进行异步计算. Future模式可以这样来描述:我有一个任务,提交给了Future,Future替我完成这个任务.期间我自己可以去做任何想做的事情.一段时 间之后,我就便可以从Future那儿取出结果.就相当

Executor框架(七)Future 接口、FutureTask类

Future接口介绍 ??Future 表示异步计算的结果.它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果. ??Future 一般由 ExecutorService 的submit().invokeAll()方法返回的,用于跟踪.获取任务在线程池中的运行情况.等待运算结果,还可以取消任务.(还有其子接口 ScheduleFuture 则由 ScheduleExecutorService 的schedule()等方法返回); 方法描述 boolean cancel(boole

浅谈Java Future接口

Java项目编程中,为了充分利用计算机CPU资源,一般开启多个线程来执行异步任务.但不管是继承Thread类还是实现Runnable接口,都无法获取任务执行的结果.JDK 5中引入了Callable和Future,通过它们执行异步任务可以获取执行结果.FutureTask分析 JDK 5中获取任务执行的结果主要是通过FutureTask类实现的.FutureTask实现了RunnableFuture的接口,它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值