2021-08-30

本文详细介绍了Java中使用Callable和FutureTask进行多线程编程的方法。Callable接口允许创建有返回值的线程,而FutureTask则用于包装Callable对象并获取执行结果。通过Future接口,我们可以取消任务、检查任务状态或获取执行结果。示例代码展示了如何创建Callable线程,启动线程并在主线程中获取返回值。
摘要由CSDN通过智能技术生成

**

学习总结

通过 Callable 创建线程
通过 Future 创建线程

学习产出:

  1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。

  2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。

  3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

  4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

1、创建执行线程的方式:

实现Callable接口。相较于实现Runnable接口的方式,方法可以有返回值,并且可以抛出异常

执行Callable方式,需要FutureTask实现类的支持,用于接收运算结果

2 传统的runnable接口的定义

runnable只提供了一个返回void的run方法,而且执行失败也不会抛出异常。

这就导致使用runnable方式创建的线程执行时我们无法获取到返回值,而且失败我们也无法获取到具体的失败信息。

而且提交到线程池的Runnable任务时无法取消的,这就意味着如果我们想要取消某个任务是不可能的,只能关闭整个线程池。

从Callable接口的定义可以看出其支持泛型,返回值类型为传入的泛型参数。而且支持抛出异常

3

public class CallableThreadTest implements Callable<Integer> {
    public static void main(String[] args)  
    {  
        CallableThreadTest ctt = new CallableThreadTest();  
        FutureTask<Integer> ft = new FutureTask<>(ctt);  
        for(int i = 0;i < 100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);  
            if(i==20)  
            {  
                new Thread(ft,"有返回值的线程").start();  
            }  
        }  
        try  
        {  
            System.out.println("子线程的返回值:"+ft.get());  
        } catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        } catch (ExecutionException e)  
        {  
            e.printStackTrace();  
        }  
  
    }
    @Override  
    public Integer call() throws Exception  
    {  
        int i = 0;  
        for(;i<100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" "+i);  
        }  
        return i;  
    }  
}

4 Future接口的定义

尝试去取消整个任务,可能还失败如果该任务已经完成或者已经被取消,或者由于其他原因不能被取消。

  • 取过取消任务成功,整个任务的cancel方法被调用时任务还没有开始。如果这个任务已经启动了,参数
  • mayInterruptIfRunning 决定了是否给执行任务的线程发送中断信号。
  • 这个方法返回后,后续的isDone调用总是返回true。
  • 后续的isCancelled调用总是返回true,如果这个方法返回了true。

5 Future接口提供了4个方法

boolean cancel(boolean mayInterruptIfRunning);

boolean isCancelled();

boolean isDone();

V get() throws InterruptedException, ExecutionException;

V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;

6 get

get方法用于获取Callable的执行结果,通常有5中情况发生

任务已经正常结束,此时获取到Callable的返回结果。

任务还没有结束,这是很有可能的,因为我么你通常将任务放到线程池中,有可能你调用get方法是,任务还在任务队列中没被执行呢,另外一种情况是任务已经执行了,但是可能需要很长时间才能返回,
这两种情况下,调用get方法时自然是获取不到结果的,都会阻塞当前线程。知道任务完成返回结果。

任务执行过程中被cancel,这个时候任务会抛出CancellationException异常

任务执行过程中失败,这个时候任务会抛出ExecutionException异常。

任务超时。get方法有个带有参数的重载方法。调用带有延迟参数的get方法后,如果在指定时间内任务执行完毕,返回结果,如果任务无法完成工作,直接抛出TimeoutException异常。

7 isDone

用来判断任务是否执行完毕

如果任务完成了返回true。如果任务没有完成返回false。

需要注意的是该方法返回true不代表任务是成功执行了,只代表任务结束了。如果任务执行过程中被取消了。该方法依旧返回true。因为该任务确实执行完毕了,以后不会在被执行了。

8 用 FutureTask 来创建 Future

典型用法是,把 Callable 实例当作 FutureTask 构造函数的参数,生成 FutureTask 的对象,然后把这个对象当作一个 Runnable 对象,这里使用了桥接的设计模式。放到线程池中或另起线程去执行,最后还可以通过 FutureTask 获取任务执行的结果。但是使用Future获取结果时需要注意,get方法在没有任务没有执行完毕时会阻塞调用者线程。

import java.util.concurrent.*;
 
public class FutureTaskDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        FutureTask<Integer> futureTask = new FutureTask<>(new Task());
        executorService.submit(futureTask);
        System.out.println(futureTask.get());
        executorService.shutdown();
    }
 
    static class Task implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            int num = 0;
            for (int i = 0; i < 10000; i++) {
                num = num + i;
            }
            return num;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值