ITPub博客

首页 > 数据库 > 数据库开发技术 > 面试官:说下execute 与 submit 方法的内存消耗对比

面试官:说下execute 与 submit 方法的内存消耗对比

原创 数据库开发技术 作者:IT枫斗者 时间:2024-12-09 17:32:01 0 删除 编辑

在Java开发中,线程池(ThreadPoolExecutor)是处理并发任务的利器。提交任务到线程池时,常用的有两种方式:execute 和 submit。你是否想过,这两种方法在内存使用上有什么不同?


本文将通过源码分析,详细探讨它们各自的内存占用情况,帮助你在实际项目中做出更优选择。


 execute 

VS

 submit 



execute 与 submit 的基本区别


execute 方法


用途:

提交一个 Runnable 任务,不会返回执行结果。


适用场景:

当你只需要执行任务,而不需要获取结果时使用。


submit 方法

用途:

提交一个 Runnable 或 Callable 任务,并返回一个 Future 对象,用于获取任务的执行结果或取消任务。

适用场景:

需要获取任务结果或在未来某个时刻取消任务时使用。




内存占用分析


使用 execute 提交任务

假设我们向线程池提交了20万个空任务,代码如下:

public static void main(String[] args) {    ThreadPoolExecutor threadPoolExecutor =            new ThreadPoolExecutor(8, 8, 15, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());    for (int i = 0; i < 200000; i++) {        threadPoolExecutor.execute(() -> {            LockSupport.park();        });    }    LockSupport.park();}


内存计算

Lambda实例:在启用指针压缩的情况下,每个空的Lambda实例大约占用16字节。

LinkedBlockingQueue 的 Node 对象:每个节点大约占用24字节。

每个任务总共:约40字节。

20万任务:总内存约7.6MB。
如果改用 ArrayBlockingQueue,由于省去了 Node 对象的开销,每个任务只需16字节,总内存约3.05MB。


使用 submit 提交任务

同样提交20万个空任务,代码如下:

public static void main(String[] args) {    ThreadPoolExecutor threadPoolExecutor =            new ThreadPoolExecutor(8, 8, 15, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());    for (int i = 0; i < 200000; i++) {        threadPoolExecutor.submit(() -> {            LockSupport.park();        });    }    LockSupport.park();}


内存计算

FutureTask 对象:约32字节。

RunnableAdapter 对象:约24字节。

Lambda实例:16字节。

LinkedBlockingQueue 的 Node 对象:24字节。

每个任务总共:约96字节。

20万任务:总内存约18.31MB。
使用 ArrayBlockingQueue 时,每个任务约72字节,总内存约10.94MB。




优化建议


1. 选择合适的方法:

不需要结果:优先选择 execute 方法,节省内存开销。
需要结果:使用 submit 方法,但需考虑额外的内存消耗。


2. 选择合适的队列:

LinkedBlockingQueue:适用于任务量较大且不频繁取出任务的场景,但内存占用较高。
ArrayBlockingQueue:更节省内存,适合任务量固定的情况。


3. 减少对象创建:

尽量避免在Lambda中捕获外部变量,这样可以复用实例,降低内存使用。




实际案例分析



假设我们使用 execute 方法并选择 ArrayBlockingQueue,向线程池提交20万个空任务时,内存占用约为3.05MB。而使用 submit 方法,同样条件下内存占用约为10.94MB。
这表明,submit 方法由于需要额外的 FutureTask 和 RunnableAdapter 对象,内存开销显著增加。


此外,如果在Lambda表达式中没有使用到外部变量,系统只会创建一个Lambda实例,从而进一步减少内存占用。这在高并发和大任务量的场景下尤为重要。



结论


在高并发和大量任务的应用场景中,选择合适的任务提交方式和阻塞队列对于优化内存使用至关重要。

execute 方法由于不需要额外的包装对象,内存占用较低,适合大规模任务提交。

而 submit 方法虽然提供了获取任务结果的能力,但会带来更高的内存开销。

根据具体需求选择合适的方法和数据结构,可以有效提升系统性能和资源利用率。

👋 大家好,我是枫哥,一名Java后端开发者!我热衷于探索新技术,并在我的微信公众号上分享关于Java 生态和后端开发的知识。欢迎关注我的公众号,期待与你一起探讨技术的无穷可能!” 

目前专注于Java技术分享,覆盖春招、秋招、社招和跳槽相关内容,并提供一对一带徒学习服务。加入 学徒计划,即可享受内推机会和优质资源,签订协议确保就业无忧。

此外,我们还推出了‘Java跳槽网’, 为你的求职之路提供全方位支持,助你快速找到理想工作




—END—



原文作者:IT枫斗者;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论
大家好,我是枫哥,🌟阿里云技术专家、📝资深面试官、🌹Java跳蚤网课堂创始人。技术交流vx:itfdz666,拥有多年一线研发经验,曾就职过科大讯飞、美团网、平安等公司。看面试实战视频可以关注b站号:IT枫斗者–Java面试。需要看技术文章,可以关注CSDN:IT枫斗者。

注册时间:2020-05-13

  • 博文量
    183
  • 访问量
    81352