多线程-Callable、Future、FutureTask

我们普遍知道的创建线程的方式有两种,一种是继承Thread,一种是实现Runnable接口。这两种方式都无法获取任务执行完后的结果,并发包提供了Callable 类能够得到任务执行完的结果。

为何需要Future与Callable的模式?我们先用常用方式来实现需求。获取线程执行完后的结果。

public class Demo1 {
    public static void main(String[] args) {
        Callable callable = new Callable() {
            @Override
            public void call(int num) {
                System.out.println("线程运行结果:"+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("主线程结束");
    }
}
interface Callable{
    void call(int num);
}

  一般我们使用回调的方式来获取结果。但这种方式有三个缺点:

  1、必须要有回调接口。并根据线程运行情况,接口至少要拥有成功回调和错误回调两个方法。

  2、当线程运行后,只能等到线程回调接口,开发者没办法进行取消操作。

  3、如果要重复获取同样线程运行结果,只能重新运行线程。或缓存一个变量结果值。

  因此java提供了Future与Callable的模式


 Callable:

  位于java.util.concurrent包下的一个接口,声明了一个call()方法。

public interface Callable<V> {
    // 计算结果,如果无法计算结果,则抛出一个异常
    V call() throws Exception;
}

   Callable一般与ExecutorService配合使用。在ExecutorService接口中声明了几个submit方法的重载:

//提交一个实现Callable接口的任务,并且返回封装了异步计算结果的Future
<T> Future<T> submit(Callable<T> task);
//提交一个实现Runnable接口的任务,并且指定了在调用Future的get方法时返回的result对象
<T> Future<T> submit(Runnable task, T result);
//提交一个实现Runnable接口的任务,并且返回封装了异步计算结果的Future
Future<?> submit(Runnable task);

  对比Runnable:

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

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

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

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

 (5)  加入线程池运行时,Runnable使用的是ExecutorService的execute方法,而Callable使用的submit方法。 

Future:

  Future可以对具体的Runnable或Callable任务的执行结果进行取消,查询是否完成、获取结果。可通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

  Future是位于java.util.concurrent包下的一个接口:

public interface Future<V> {
    // 试图取消对此任务的执行,参数表示是否允许取消正在执行却未执行完的任务
    boolean cancel(boolean mayInterruptIfRunning);
    // 如果在任务正常完成前将其取消,则返回true
    boolean isCancelled();
    // 如果任务已完成,则返回true
    boolean isDone();
    // 获取执行结果,产生阻塞直到任务执行完毕才返回
    V get() throws InterruptedException, ExecutionException;
    // 用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

   Futre提供的三种功能:判断任务是否完成、能够中断任务、能够获取 任务结果。Future是接口,无法直接创建对象来使用,因此就有了FutureTask。


 FutureTask:

  FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable接口和Future接口。所以它可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。它是Future接口的一个唯一实现类。

  FutureTask的状态:

    1、未启动:当FutureTask.run()方法未被执行前,处于未启动状态。当创建一个FutureTask但run方法未执行前,也处于未启动状态。

    2、已启动:FutureTask.run()被执行的过程中,处于已启动状态。

    3、已完成:FutureTask.run()执行完正常结束或被取消或抛出异常而结束,都处于完成状态。

   FutureTask的执行:

  

    1、未启动或已启动状态下,执行FutureTask.get()方法会导致调用线程阻塞。已完成状态下,执行该方法调用线程会立即返回结果或抛出异常。

    2、未启动状态下,执行FutureTask.cancel()方法将导致任务永不执行。已启动状态下,执行cancel(true)方法将以中断执行此任务线程的方式来试图停止任务。取消成功返回true。但如果执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时cancel(...)返回false。当任务已经完成,执行cancel(...)方法将返回false。


使用实例:

Callable实现类

/**
 * @Title: CallableServiceImpl
 * @Description: Callable实现类
 * @date 2019/1/1814:18
 */
public class CallableServiceImpl implements Callable<Integer> {
    private static final Logger logger = LoggerFactory.getLogger(CallableServiceImpl.class);
    private int sum;
    @Override
    public Integer call() throws Exception {
        logger.info("callable子线程开始计算");
        Thread.sleep(2000);
        for (int i = 0; i < 100; i++) {
            sum = sum + i;
        }
        logger.info("callable子线程结束计算");
        return sum;
    }
}

 Callable+Future

/**
 * @Title: FutureClient
 * @Description:
 * @date 2019/1/1814:21
 */
public class FutureClient {
    private static final Logger logger = LoggerFactory.getLogger(FutureClient.class);

    public static void main(String[] args) {
        //创建线程池
        ExecutorService es = Executors.newSingleThreadExecutor();
        //创建任务
        CallableServiceImpl task = new CallableServiceImpl();
        //提交任务并获取执行结果
        Future<Integer> future = es.submit(task);
        //关闭线程池
        es.shutdown();
        try {
            Thread.sleep(2000);
            if (future.get()!=null){
                logger.info("结果是:"+future.get());
            }else {
                logger.info("未获取到结果");
            }
        } catch (Exception e) {
            logger.error("FutureClient error {}",e);
        }
        logger.info("主线程执行完成");
    }
}

 Callable+FutureTask

/**
 * @Title: FutureTaskClient
 * @Description:
 * @date 2019/1/1814:26
 */
public class FutureTaskClient {
    private static final Logger logger = LoggerFactory.getLogger(FutureTaskClient.class);

    public static void main(String[] args) {
        //创建线程池
        ExecutorService es = Executors.newSingleThreadExecutor();
        //创建任务
        CallableServiceImpl task = new CallableServiceImpl();
        //提交任务
        FutureTask<Integer> futureTask = new FutureTask<>(task);
        es.submit(futureTask);
        //关闭线程池
        es.shutdown();
        try {
            Thread.sleep(2000);
            if (futureTask.get()!=null){
                logger.info("结果是:"+futureTask.get());
            }else {
                logger.info("未获取到结果");
            }
        } catch (Exception e) {
            logger.error("FutureTaskClient error {}",e);
        }
        logger.info("主线程执行完成");
    }
}

 

 

 

 

 

 

 

 

 

 

   

转载于:https://www.cnblogs.com/zhangbLearn/p/10081070.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值