Java多线程并发笔记(6)Future与Callable

前言

当被Netty吊打时,又回来继续攀知识树

Callable

Callable很简单,和Runnable类似,都是用于启动线程的接口
区别在与:

  • Runnable没有返回值;Callable可以返回执行结果,是个泛型< V>,和Future、FutureTask配合可以用来获取异步执行的结果(当然Runnable也可以异步执行)
  • Callable接口的call()方法允许抛出异常;Runnable的run()方法异常只能在内部消化,不能往上继续抛
  • Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞

在这里插入图片描述
使用方法:implements Callable接口,然后重写call()方法即可,call()方法返回Object对象(即任何类型都可返回)

public class Task implements Callable {
    private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    @Override
    public Object call() throws Exception {
        System.out.println("任务运行中。。。时间:"+sdf.format(new Date()));
        Thread.sleep(8000);
        int num = 100;
        return num;
    }
}

完成一个异步案例

package com.company.javaBasis.Future;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;

public class Task implements Callable {
    private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    @Override
    public Integer call() throws Exception {
        System.out.println("任务运行中。。。时间:"+sdf.format(new Date()));
        Thread.sleep(8000);
        int num = 100;
        return num;
    }
}

class Test{
    private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");

    public static void main(String[] args) {

        //创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        Task task = new Task();
        Future future = executorService.submit(task);
        //关闭线程池,还在运行的任务继续运行
        executorService.shutdown();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("主线程运行中。。。时间:"+sdf.format(new Date()));

        try {
            System.out.println("Task 运行结果:"+future.get()+",时间:"+sdf.format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("所有任务运行完毕。。。时间:"+sdf.format(new Date()));
    }
}

在这里插入图片描述

我们程序的运行应该是:
Task任务(阻塞8秒) - 》 阻塞5秒 - 》 输出Task结果

根据输出的时间可以看出,在Task阻塞8秒的同时,主线程阻塞5秒也在进行中,所以一共阻塞了8秒,这就是异步操作
future.get()也阻塞了主线程,让主线程等待Task结果

Future

从JDK1.5开始,提供了Callable和Future,通过它们可以异步的在任务执行完毕之后得到任务执行结果

同步:运行时必须等待上一步执行完成,才能进行到下一步
异步:运行时不必等待上一步执行完成,立刻执行下一步

网页常用的AJAX就是个很典型的异步技术

Future是一个未来结果,可以对线程任务的执行结果进行取消、查询是否完成、获取结果
即可以先得到Future对象,然后随着线程任务的执行,将结果赋给Future对象

Future定义了5个方法:
在这里插入图片描述

  1. cancel方法:试图取消任务的执行
    取消成功返回true,失败返回false
    mayInterruptIfRunning参数表示是否允许取消正在执行却没有执行完毕的任务
  2. get()方法
    获得任务结果,这个方法会将线程阻塞
  3. get(long timeout,TimeUnit unit)
    获得任务结果,设置阻塞时间
    例如上面的例子设置
future.get(1000,TimeUnit.MILLISECONDS)

只在这里等待一秒结果,明显是等不到结果了,因为Task要阻塞8秒
在这里插入图片描述

  1. isCancelled方法
    表示任务是否被取消成功,如果在任务正常完成前被取消成功,返回 true
  2. isDone方法
    表示任务是否已经完成,若任务完成,则返回true

从这五个方法中可以看出Future提供3种功能:判断任务是否完成;中断任务;获得任务结果
(功能比较弱,所以Netty中改进了Future,增加了很多功能)

FutureTask

FutureTask是Future的实现类
我们可以Debug看看:
在这里插入图片描述
FutureTask定义了这些方法
在这里插入图片描述
FutureTask是一个可以取消的异步结果,提供了 Future的实现
在这里插入图片描述在这里插入图片描述

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

所以前面的案例如果用FutureTask实现,可以省去线程池或者将FutureTask注册进线程池
在这里插入图片描述

总结

  1. Callable可以作为一种线程的接口,和Runnable不同的是,Callable有返回值,且可以抛出异常
  2. Future是未来结果,对于线程,可以立即得到Future对象,然后等线程执行完了,再从Future对象中得到结果,可以通过Future.get()方法阻塞获得结果
  3. FutureTask是Future的实现类,即能作为一个线程,也可以作为Future
发布了95 篇原创文章 · 获赞 25 · 访问量 4179

猜你喜欢

转载自blog.csdn.net/key_768/article/details/104869727