Future、FutureTask、Runnable、Callable的关系

本文借鉴了这篇文章以及这篇文章并在此基础上加了一些其他知识点。

一、Runnable

1、实现该接口并重写run方法
2、利用该类的对象创建线程
3、线程启动时就会自动调用该对象的run方法

通常在开发中结合ExecutorService使用,将任务的提交与任务的执行解耦开,同时也能更好地利用Executor提供的各种特性

缺点:

1.Runnable接口不能抛出异常,只能内部消化;
2.Runnable接口没有返回值

注意:
因为Java单继承多实现,所以相对于集成Thread类,实现Runnable接口要更好。

ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new Runnable() 
{ 
     public void run() 
     {
        //TODO
     }
 });
executor.shutdown();

ExecutorService继承(扩展)了Executor接口,添加了一些用于生命周期管理的方法。

public interface ExecutorService extends Executor{
	void shutdown();//平稳的关闭,不再接收新的任务、同时等待已经提交的任务执行
	List<Runnable> shutdownNow();//执行暴力的关闭,尝试取消所有运行的任务,并且不再启动队列中尚未启动的任务
	boolean isShutDown();//如果此执行程序已关闭,则返回 true
	boolean isTerminated();//查询ExecutorService是否已经终止
	boolean awaitTermination(long timeout, TimeUint unit) throws InterruptedException;
	//通常调用awaitTermination后会立即调用shutdown,产生同步关闭ExecutorServicce
	Future<?> submit(Runnable task);
	//提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功 完成时将会返回 null。 抛出RejectedExecutionException - 如果任务无法安排执行 ,NullPointerException - 如果该任务为 null
	.....
}

二、Callable接口

与Runnable不同的是,Callable是个泛型参数化接口,并能返回线程的执行结果,且能在无法正常计算时抛出异常

public interface Callable<V> 
{
    V call() throws Exception;
}

Callable并不像Runnable那样通过Thread的start方法就能启动实现类的run方法,所以它通常利用ExecutorService的submit方法去启动call方法自执行任务,而ExecutorService的submit又返回一个Future类型的结果,因此Callable通常也与Future一起使用

ExecutorService pool = Executors.newCachedThreadPool();
Future<String> future = pool.submit(new Callable{
      public void call(){
              //TODO
      }
});

Runnable与Callable不同点:

  1. Runnable不返回任务执行结果,Callable可返回任务执行结果
  2. Callable在任务无法计算结果时抛出异常,而Runnable不能
  3. Runnable任务可直接由Thread的start方法或ExecutorService的submit方法去执行

三、Future

Future保存异步计算的结果,可以在我们执行任务时去做其他工作,并提供了以下几个方法

  • cancel(boolean mayInterruptIfRunning):试图取消执行的任务,参数为true时直接中断正在执行的任务,否则直到当前任务执行完成,成功取消后返回true,否则返回false
  • isCancel():判断任务是否在正常执行完前被取消的,如果是则返回true
  • isDone():判断任务是否已完成
  • get():等待计算结果的返回,如果计算被取消了则抛出
  • get(long timeout,TimeUtil unit):设定计算结果的返回时间,如果在规定时间内没有返回计算结果则抛出TimeOutException

使用Future的好处:

  1. 获取任务的结果,判断任务是否完成,中断任务
  2. Future的get方法很好的替代的了Thread.join或Thread,join(long millis)
  3. Future的get方法可以判断程序代码(任务)的执行是否超时,如:
try{
      future.get(60,TimeUtil.SECOND);
 }catch(TimeoutException timeout){
      log4j.log("异常,将被取消!!");
      future.cancel();
 }

四、FutureTask

先看FutureTask的实现

public class FutureTask<V> implements RunnableFuture<V>

FutureTask实现了RunnableFuture接口,我们再来看下RunnableFuture接口:

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

可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
FutureTask提供了2个构造器:

public FutureTask(Callable<V> callable) 
{
}
public FutureTask(Runnable runnable, V result) 
{
}

代码例子

1、Callable+Future获取执行结果
public class Test {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();
        Future<Integer> result = executor.submit(task);
        executor.shutdown();
         
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
         
        System.out.println("主线程在执行任务");
         
        try {
            System.out.println("task运行结果"+result.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
         
        System.out.println("所有任务执行完毕");
    }
}
class Task implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("子线程在进行计算");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++)
            sum += i;
        return sum;
    }
}
2、Callable+FutureTask获取执行结果
public class Test {
    public static void main(String[] args) {
        //第一种方式
        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
        executor.submit(futureTask);
        executor.shutdown();
         
        //第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread
        /*Task task = new Task();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
        Thread thread = new Thread(futureTask);
        thread.start();*/
         
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
         
        System.out.println("主线程在执行任务");
         
        try {
            System.out.println("task运行结果"+futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
         
        System.out.println("所有任务执行完毕");
    }
}
class Task implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("子线程在进行计算");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++)
            sum += i;
        return sum;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值