java线程获取结果Callable、Future、FutureTask

java线程获取结果Callable、Future、FutureTask

一、提个问题

     当我们创建一个线程时,我们想获取线程运行完成后的结果,是否可以用回调的方法来实现?

答案是肯定的,可以。例如:

//定义一个回调接口
interface Callable {
    void call(int num);
}


public class FutureTest {


    public static void main(String[] args)  {
         
       //创建回调对象
        Callable callable = new Callable() {
            @Override
            public void call(int num) {
                System.out.println("线程运行结果值 num=="+num);
            }
        };

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程"+Thread.currentThread().getName()+" 开始");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //调用回调方法  把结果传出去
                callable.call(100);
                System.out.println("线程"+Thread.currentThread().getName()+" 结束");
            }
        }, "t1").start();

        System.out.println("主线程结束");

    }
}

运行结果:

主线程结束
线程t1 开始
线程运行结果值 num==100
线程t1 结束

这种方式的实现有三个缺点:

1、必须要创建回调接口。而且线程运行中可能产生异常,那么回调接口至少包含成功回调和错误回调两个方法。
2、当线程运行后,只能等到线程回调接口,本身我们没有办法进行取消操作。
3、如果要重复获取同样线程运行结果的值,还是只能重新运行线程。当然你也可以使用一个变量缓存结果值。

那么有没有一种优化的方式呢?java中提供了FutureCallable的模式。

 

二、理解 Runnable、CallableFuture

    先看一下定义

   Runnable

          Runnable它只有一个run()函数,用于将耗时操作写在其中,该函数没有返回值。然后使用某个线程去执行该Runnable即可实现多线程,Thread类在调用start()函数后就是执行的是Runnablerun()函数.

public interface Runnable {
    /*
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

   Callable

           Callable中有一个call()函数,但是call()函数有返回值,而Runnable的run()函数不能将结果返回给客户程序。

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

 Callable 与Runnable的区别  

    1、Callable定义的方法是call,而Runnable定义的方法是run。
    2、Callable的call方法可以有返回值,而Runnable的run方法不能有返回值。
    3、 Callable的call方法可抛出异常,而Runnable的run方法不能抛出异常

 

   Future

           Executor就是RunnableCallable的调度容器,Future就是对于具体的Runnable或者Callable任务的执行结果进行

取消、查询是否完成、获取结果、设置结果操作进行的再次封装。注意其get方法会阻塞,直到任务返回结果。

public interface Future<V> {
 
    /**
     * Attempts to cancel execution of this task.  This attempt will
     * fail if the task has already completed, has already been cancelled,
     * or could not be cancelled for some other reason. If successful,
     * and this task has not started when <tt>cancel</tt> is called,
     * this task should never run.  If the task has already started,
     * then the <tt>mayInterruptIfRunning</tt> parameter determines
     * whether the thread executing this task should be interrupted in
     * an attempt to stop the task.
     */
    boolean cancel(boolean mayInterruptIfRunning);
 
    /**
     * Returns <tt>true</tt> if this task was cancelled before it completed
     * normally.
     */
    boolean isCancelled();
 
    /**
     * Returns <tt>true</tt> if this task completed.
     *
     */
    boolean isDone();
 
    /**
     * Waits if necessary for the computation to complete, and then
     * retrieves its result.
     *
     * @return the computed result
     */
    V get() throws InterruptedException, ExecutionException;
 
    /**
     * Waits if necessary for at most the given time for the computation
     * to complete, and then retrieves its result, if available.
     *
     * @param timeout the maximum time to wait
     * @param unit the time unit of the timeout argument
     * @return the computed result
     */
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

      Future<V>接口是用来获取异步计算结果的,说白了就是对具体的Runnable或者Callable对象任务执行的结果进行获取(get()),取消(cancel()),判断是否完成等操作

Future接口中声明了5个方法,下面依次解释每个方法的作用:   

  1、cancel方法  用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。

参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。

如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;

如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。

   2、isCancelled方法  表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。

   3、isDone方法   表示任务是否已经完成,若任务完成,则返回true;

   4、get()方法    用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

   5、get(long timeout, TimeUnit unit)    用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

也就是说Future提供了三种功能:   

1)判断任务是否完成;

2)能够中断任务;

3)能够获取任务执行结果。

因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask

 

FutureTask

看一下FutureTask的实现类

public class FutureTask<V> implements RunnableFuture<V>

说明FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现

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

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

 

另外FutureTaslk还可以包装RunnableCallable<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类型的任务

因此FutureTask是Future也是Runnable,又是包装了的Callable( 如果是Runnable最终也会被转换为Callable )。

 

Callable 和 Future接口的区别

 1、Callable规定的方法是call(),而Runnable规定的方法是run(). 
 2、Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。  
 3、call()方法可抛出异常,而run()方法是不能抛出异常的。 
 4、运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。 
 5、它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。 
 6、 通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。 
 7、Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。


三、解决问题

      搞清楚了概念我们就来解决文章开头提出的问题

    1、使用Callable+Future获取执行结果

package com.demo.test;

import java.util.concurrent.Callable;

//定义一个任务
public 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;
    }

}

 

package com.demo.test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CallableTest {

    public static void main(String[] args) {
        //创建线程池
        ExecutorService executor = Executors.newCachedThreadPool();
        //创建Callable对象任务  
        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 {
            if(result.get()!=null){  
                System.out.println("task运行结果"+result.get());
            }else{
                System.out.println("未获取到结果"); 
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
         
        System.out.println("所有任务执行完毕");
    }
}

  2、使用Callable+FutureTask获取执行结果

package com.demo.test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class CallableTest1 {
    
    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 {
            if(futureTask.get()!=null){  
                System.out.println("task运行结果"+futureTask.get());
            }else{
                System.out.println("future.get()未获取到结果"); 
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
         
        System.out.println("所有任务执行完毕");
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java线程获取返回结果,可以使用以下两种方式: 1.使用CallableFuture Callable接口类似于Runnable接口,但是它可以返回一个结果,并且可以抛出异常。Future接口表示异步计算的结果,提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。 下面是一个使用CallableFuture获取返回结果的例子: ```java import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableDemo { public static void main(String[] args) throws ExecutionException, InterruptedException { Callable<Integer> callable = new Callable<Integer>() { @Override public Integer call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; } return sum; } }; FutureTask<Integer> futureTask = new FutureTask<>(callable); new Thread(futureTask).start(); System.out.println("计算结果为:" + futureTask.get()); } } ``` 2.使用join方法 join方法是Thread类提供的方法,它可以让一个线程等待另一个线程执行完成。在等待的过程中,当前线程会被阻塞,直到另一个线程执行完成。 下面是一个使用join方法获取返回结果的例子: ```java public class JoinDemo { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Runnable() { @Override public void run() { int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; } System.out.println("计算结果为:" + sum); } }); thread.start(); thread.join(); System.out.println("线程执行完成"); } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值