50 Future使用避坑指南:这些注意事项你必须知道

图片[1]-50 Future使用避坑指南:这些注意事项你必须知道 - 速优课-速优课

Future使用避坑指南:这些注意事项你必须知道

📌 本文导读

本文将围绕「使用 Future 有哪些注意点?Future 产生新的线程了吗?」这一主题展开深入探讨。

在Java并发编程的学习过程中,这是一个非常核心且高频的知识点。通过本文的学习,你将能够:

✅ 理解核心概念与原理 ✅ 掌握实际应用场景 ✅ 避开常见的使用陷阱 ✅ 应对面试中的相关问题


在本课时我们将讲解使用 Future 有哪些注意点,以及 Future 产生新的线程了吗?

Future 的注意点

1. 当 for 循环批量获取 Future 的结果时容易 block,get 方法调用时应使用 timeout 限制

对于 Future 而言,第一个注意点就是,当 for 循环批量获取 Future 的结果时容易 block,在调用 get 方法时,应该使用 timeout 来限制。

下面我们具体看看这是一个什么情况。

首先,假设一共有四个任务需要执行,我们都把它放到线程池中,然后它获取的时候是按照从 1 到 4 的顺序,也就是执行 get() 方法来获取的,代码如下所示:

​
public class FutureDemo {
​
    public static void main(String[] args) {
​
        //创建线程池
​
        ExecutorService service = Executors.newFixedThreadPool(10);
​
        //提交任务,并用 Future 接收返回结果
​
        ArrayList<Future> allFutures = new ArrayList<>();
​
        for (int i = 0; i < 4; i++) {
​
            Future<String> future;
​
            if (i == 0 || i == 1) {
​
                future = service.submit(new SlowTask());
​
            } else {
​
                future = service.submit(new FastTask());
​
            }
​
            allFutures.add(future);
​
        }
​
        for (int i = 0; i < 4; i++) {
​
            Future<String> future = allFutures.get(i);
​
            try {
​
                String result = future.get();
​
                System.out.println(result);
​
            } catch (InterruptedException e) {
​
                e.printStackTrace();
​
            } catch (ExecutionException e) {
​
                e.printStackTrace();
​
            }
​
        }
​
        service.shutdown();
​
    }
​
    static class SlowTask implements Callable<String> {
​
        @Override
​
        public String call() throws Exception {
​
            Thread.sleep(5000);
​
            return "速度慢的任务";
​
        }
​
    }
​
    static class FastTask implements Callable<String> {
​
        @Override
​
        public String call() throws Exception {
​
            return "速度快的任务";
​
        }
​
    }
​
}
​

可以看出,在代码中我们新建了线程池,并且用一个 list 来保存 4 个 Future。其中,前两个 Future 所对应的任务是慢任务,也就是代码下方的 SlowTask,而后两个 Future 对应的任务是快任务。慢任务在执行的时候需要 5 秒钟的时间才能执行完毕,而快任务很快就可以执行完毕,几乎不花费时间。

在提交完这 4 个任务之后,我们用 for 循环对它们依次执行 get 方法,来获取它们的执行结果,然后再把这个结果打印出来。

执行结果如下:

​
速度慢的任务
​
速度慢的任务
​
速度快的任务
​
速度快的任务
​
图片[2]-50 Future使用避坑指南:这些注意事项你必须知道 - 速优课-速优课

这里有一个问题,即第三个的任务量是比较小的,它可以很快返回结果,紧接着第四个任务也会返回结果。但是由于前两个任务速度很慢,所以我们在利用 get 方法执行时,会卡在第一个任务上。也就是说,虽然此时第三个和第四个任务很早就得到结果了,但我们在此时使用这种 for 循环的方式去获取结果,依然无法及时获取到第三个和第四个任务的结果。直到 5 秒后,第一个任务出结果了,我们才能获取到,紧接着也可以获取到第二个任务的结果,然后才轮到第三、第四个任务。

假设由于网络原因,第一个任务可能长达 1 分钟都没办法返回结果,那么这个时候,我们的主线程会一直卡着,影响了程序的运行效率。

此时我们就可以用 Future 的带超时参数的 get(long timeout, TimeUnit unit) 方法来解决这个问题。这个方法的作用是,如果在限定的时间内没能返回结果的话,那么便会抛出一个 TimeoutException 异常,随后就可以把这个异常捕获住,或者是再往上抛出去,这样就不会一直卡着了。

2. Future 的生命周期不能后退

Future 的生命周期不能后退,一旦完成了任务,它就永久停在了“已完成”的状态,不能从头再来,也不能让一个已经完成计算的 Future 再次重新执行任务。

图片[3]-50 Future使用避坑指南:这些注意事项你必须知道 - 速优课-速优课

这个图也是我们当时讲解所用的图,如果有些遗忘,可以回去复习一下当时的内容。这一讲,我推荐你采用看视频的方式,因为视频中会把各个路径都标明清楚,看起来会更加清晰。

Future 产生新的线程了吗

最后我们再来回答这个问题:Future 是否产生新的线程了?

有一种说法是,除了继承 Thread 类和实现 Runnable 接口之外,还有第三种产生新线程的方式,那就是采用 Callable 和 Future,这叫作有返回值的创建线程的方式。这种说法是不正确的。

其实 Callable 和 Future 本身并不能产生新的线程,它们需要借助其他的比如 Thread 类或者线程池才能执行任务。例如,在把 Callable 提交到线程池后,真正执行 Callable 的其实还是线程池中的线程,而线程池中的线程是由 ThreadFactory 产生的,这里产生的新线程与 Callable、Future 都没有关系,所以 Future 并没有产生新的线程。

以上就是本讲的内容了。首先介绍了 Future 的两个注意点:第一个,在 get 的时候应当使用超时限制;第二个,Future 生命周期不能后退;然后又讲解了 Callable 和 Future 实际上并不是新建线程的第三种方式。


🎯 总结与思考

核心要点回顾

通过本文的学习,我们深入探讨了「使用 Future 有哪些注意点?Future 产生新的线程了吗?」相关的知识。主要内容包括:

  1. 基础概念:从底层原理出发,理解核心概念的本质
  2. 实现机制:分析源码层面的实现细节
  3. 应用场景:了解在实际项目中如何正确使用
  4. 注意事项:掌握常见的坑点与避坑方法

知识延伸

并发编程是Java高级开发必须掌握的核心技能。本文涉及的知识点不仅在日常开发中经常用到,也是各大互联网公司面试的高频考点。建议大家:

  • 🔍 结合源码深入理解原理
  • 🛠️ 多动手实践,在项目中应用
  • 📚 与其他并发知识点串联学习,形成知识体系
  • 💡 多思考为什么这么设计,而不只是怎么用

思考问题

  1. 你在实际项目中遇到过哪些与本文相关的问题?
  2. 还有哪些场景可以应用本文学到的知识?
  3. 本文讲解的内容还有哪些可以拓展的方向?

💡 欢迎在评论区留言讨论,一起交流进步!

© 版权声明
THE END
喜欢就支持一下吧
点赞12
相关推荐
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容

温馨提示:
1、本内容转载于网络,版权归原作者所有!
2、本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
3、本内容若侵犯到你的版权利益,请联系我们,会尽快给予删除处理!