java多线程:callable和futureTask获取异步任务的返回值(实现并行计算)

java多线程:callable和futureTask获取异步任务的返回值(实现并行计算),第1张

java多线程:callable和futureTask获取异步任务的返回值(实现并行计算)

场景: 我们要计算1+…+10和20+…+30相加的结果,当然可以用a=1+…+10,b=20+…+30,之后resutl=a+b。 但实际上第一个任务A和第二个任务B互不影响, 我们可以使用多线程的方法,将任务A和任务B并行执行,最后将两个任务的执行结果相加。那这样怎么用java实现呢?

先提出两个问题:

  • FutureTask和callable是用来干什么的?
  • FutureTask实现并行计算(FutureTask的使用)

Future、FutureTask、Callable解释

Future、Callable是个接口,FutureTask是Future接口的实现类,通常是Callable和FutureTask一起使用达成异步任务返回值的目的。

Future接口

Future接口有5个方法:

  • cancel(boolean): 取消任务。传入的参数为表示若任务开始执行了,是否要尝试中断该线程。参数true为进行尝试中断,false为不进行中断。
  • isCanceled(): 获取该任务是否被中断了
  • isDone(): 该任务是否已经完成
  • get(): 获取该任务的返回值,若任务还未完成则会阻塞。
  • get(Long, TimeUnit): 获取任务的返回值,若任务还未完成则会阻塞。参数为指定的阻塞的最长时间,超时会抛出TimeoutException。
Callable接口

Callable只有一个call方法,实现的Callable接口的call方法则为我们要执行的异步任务。那为什么要新建一个接口而不是直接使用Runnable的run方法呢?
实际上,run方法的返回声明为void,因此我们编写的异步任务要有返回值的话,必须要另起一个call方法了。

FutureTask类

FutureTask类实现了Future和Runnable接口,是我们实现线程异步执行,并能获取执行结果的封装的实现类。FutureTask有两个构造方法:

  • FutureTask(Callable< V> callalbe): 通常的构造方法,Callable为异步任务,通过futureTask的get()方法可以阻塞地获取任务的返回值。
  • FutureTask(Runnable runnable, V result): runnable为异步任务,result保存了执行结果,当futureTask的get()方法返回的就是result对象。当然,我们若是关心任务是否执行完成而不关心任务的返回值时,可以使用该构造方法。

因此解答第一个问题: FutureTask和Callable配合使用,用来获取异步线程的执行结果(而之前Runnable不能)。

并行计算1+…+10和20+…+30的结果

接下来回到开头提出的问题,怎么用FutureTask和Callable实现并行计算两个任务呢?

1、手动new Thread()使用

Callable是个带返回结果的任务,FutureTask封装获取Callable任务的类,但具体新建线程还是离不开Thread。因此,对于开头提到的问题,我们可以这样实现:

	public static void main(String[] args) throws ExecutionException, InterruptedException {

        FutureTask futureTask1 = new FutureTask<>(() -> {
            int res = 0;                                        // 这里用lambda匿名表达式新建了一个Callable的实现
            for (int i = 0; i <= 10; i++) {
                res += i;
            }
            return res;
        });
        FutureTask futureTask2 = new FutureTask<>(() -> {
            int res = 0;
            for (int i = 20; i <= 30; i++) {
                res += i;
            }
            return res;
        });

        Thread thread1 = new Thread(futureTask1);               // FutureTask实现了Runnable接口,所以可以作为参数构造Thread()
        Thread thread2 = new Thread(futureTask2);               // 实际上FutureTask的run方法就是在运行Callable的call方法
        thread1.start();
        thread2.start();

        System.out.println(futureTask1.get() + futureTask2.get());    // 先获取任务1的值,未完成则会阻塞;获取任务2的值同理
    }
2、配合线程池使用

当然,线程池为我们封装好了线程,使用线程池实现异步计算也更加优雅:

public static void main(String[] args) throws ExecutionException, InterruptedException {

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                3,
                5,
                10,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
                
        Future future1 = threadPoolExecutor.submit(()->{   // 这里线程池submit执行的是call方法,返回的是FutureTask对象
            int res = 0;                                            // 这里用lambda匿名表达式新建了一个Callable的实现
            for (int i = 0; i <= 10; i++) {
                res += i;
            }
            return res;
        });
        Future future2 = threadPoolExecutor.submit(()->{
            int res = 0;
            for (int i = 20; i <= 30; i++) {
                res += i;
            }
            return res;
        });

        System.out.println(future1.get() + future2.get());
    }
执行结果

上述只是一个简单的示例,两位数之和性能体现不出什么。在实际测试时,将任务变为10亿级的,即0-1000000000之和加上2000000000-3000000000亿之和,若是传统的同步的方法,耗时约500ms。而使用上述的并行的方法,耗时三百多ms。符合预期。

	public static void main(String[] args){
        long startTime =System.currentTimeMillis();

        long res = 0;
        for (long i = 0; i <=1000000000 ; i++) {
            res += i;
        }
        for (long i = 2000000000; i <=3000000000L ; i++) {
            res += i;
        }
        System.out.println(res);

        long endTime = System.currentTimeMillis();
        System.out.println("执行时间为:" + (endTime - startTime));
    }

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5697273.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存