【022】❓问: Java中的Executor框架及其优点?


Java中的Executor框架及其优点:

1、简化线程管理: Executor框架通过提供线程池管理机制,简化了线程的创建和分配。

2、资源优化: 通过线程复用,减少了线程创建和销毁的性能开销。

3、提供调度和控制: Executor框架提供了灵活的线程调度和执行控制机制。


请添加图片描述

📢 王二被骂哭!new Thread () 炸服后,哇哥掏出 Executor 救场|Java 并发篇 1

请添加图片描述

零、引入

“服务器直接 OOM,订单全卡了!” 技术部传来王二的哀嚎,他刚写的 “商品库存同步” 代码上线 5 分钟就炸了,老板拿着保温杯站在他身后,脸色比 IDE 的深色模式还黑。王二屏幕上满是OutOfMemoryError,代码里清一色的new Thread()——1000 个库存同步任务,他就建了 1000 个线程。

“你这是把服务器当菜市场了?” 哇哥叼着奶茶走过来,手指戳着屏幕,“线程是重量级资源,创建销毁比你买杯奶茶还费钱,1000 个线程同时跑,内存不炸才怪。” 说着敲了三行代码,替换掉王二的new Thread(),服务器立马恢复正常。

这篇就跟着哇哥和王二,搞懂 Executor 框架到底是啥 —— 它不是 “新语法”,而是 Java 给你备好的 “线程管家”,看完你再也不用写new Thread()这种 “炸服代码”。点赞 + 关注,下次面试被问并发,你比王二稳 10 倍!
请添加图片描述

一、王二的 “炸服代码”:new Thread () 的三大死罪

王二的库存同步代码很简单:1000 个商品,每个商品开一个线程同步库存到数据库。本地跑着挺溜,一到线上就歇菜。哇哥把他的代码贴在白板上,圈出三个致命坑。

王二的 “自杀式” 代码

import java.util.concurrent.CountDownLatch;

public class ThreadOomDemo {
    // 1000个库存同步任务
    static final int TASK_NUM = 1000;
    static CountDownLatch latch = new CountDownLatch(TASK_NUM);

    public static void main(String[] args) {
        for (int i = 0; i < TASK_NUM; i++) {
            int goodsId = i; // 商品ID
            // 每个任务开一个新线程(致命操作)
            new Thread(() -> {
                try {
                    // 模拟库存同步:查数据库→改库存→写日志
                    System.out.println("同步商品" + goodsId + "库存");
                    Thread.sleep(100); // 模拟数据库耗时
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown();
                }
            }).start();
        }

        try {
            latch.await();
            System.out.println("所有库存同步完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

✔️** 哇哥怒怼三大死罪**

  • 资源耗尽罪:线程是 “重量级选手”,创建时要占 1-2MB 栈内存,1000 个线程就是 1-2GB,服务器内存直接被吃光,OOM 没商量;
  • 效率低下罪:线程创建、销毁要切换 CPU 上下文,比你下楼买包烟还费时间,1000 个线程频繁启停,CPU 全在 “打杂”,正事一点没干;
  • 管理混乱罪:线程散养,没统一管控 —— 哪个线程抛异常了?任务执行到哪了?全是糊涂账,出问题查日志查到天亮。

“你这代码就像在马路上随便拉人干活,” 哇哥喝了口奶茶,“来人就招,人多了住不下就炸;活干完了人就走,下次再招人又得重新培训 ——Executor 框架就是给你配个‘人力资源部’,线程统一招、统一管、重复用,省钱又省心。”

二、Executor 框架:Java 给你备好的 “线程管家”

哇哥擦了擦白板,画了个简单的架构图:“Executor 框架不是一个类,是一套‘线程管理体系’,核心就三个东西:顶层接口、线程池实现、工具类。你不用自己建线程,只需要把任务交给他,剩下的全不用管。”

Executor 框架核心 “三件套”(人话版)

在这里插入图片描述

“简单说,你把任务(Runnable/Callable)交给 ExecutorService,它会用池子里的线程执行,线程用完不销毁,留着下次再用 —— 这就是‘线程池’的核心思想,复用线程,减少开销。”

📲 王二的第一次优化:用 FixedThreadPool 替换 new Thread ()

请添加图片描述
哇哥先教王二用最常用的FixedThreadPool(固定数量线程池),核心是 “线程数固定,任务排队执行”,适合王二这种 “任务数量多但线程不能乱开” 的场景。

优化后的代码(再也不 OOM)

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorFixedDemo {
    static final int TASK_NUM = 1000;
    static CountDownLatch latch = new CountDownLatch(TASK_NUM);

    public static void main(String[] args) {
        // 1. 用Executors创建固定线程池:核心线程数=10(根据CPU核心数调整)
        ExecutorService threadPool = Executors.newFixedThreadPool(10);

        for (int i = 0; i < TASK_NUM; i++) {
            int goodsId = i;
            // 2. 提交任务给线程池,不用自己new Thread()
            threadPool.submit(() -> {
                try {
                    System.out.println("同步商品" + goodsId + "库存,执行线程:" + Thread.currentThread().getName());
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown();
                }
            });
        }

        try {
            latch.await();
            System.out.println("所有库存同步完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 3. 关闭线程池:先拒绝新任务,等现有任务跑完再关闭
            threadPool.shutdown();
        }
    }
}

运行效果:线程重复用,内存稳如狗

王二跑起来发现,控制台里线程名全是pool-1-thread-1到pool-1-thread-10,1000 个任务靠 10 个线程就搞定了,内存占用连之前的 1/10 都不到。

程池里的线程是‘复用’的,” 哇哥指着日志,“第一个任务用完线程 1,线程 1 不会死,接着执行第二个任务,这样 10 个线程就能扛 1000 个任务,内存能炸才怪。”

三、Executors 的 “三大常用线程池”:按需挑,不用瞎造

哇哥说,Executors 给你备了 “三款现成的线程池”,就像外卖平台的 “套餐”,不用你自己买菜做饭,按需选就行。他给王二列了个 “点餐指南”。

1. FixedThreadPool(固定线程数)

  • 特点:线程数固定,任务多了就放队列里排队;
  • 核心参数:只需要传 “核心线程数”(比如 10);
  • 场景:任务量大但并发不能太高(比如库存同步、订单处理);
  • 代码:Executors.newFixedThreadPool(10)。

2. CachedThreadPool(缓存型线程池)

  • 特点:线程数 “按需增减”,没任务时线程会在 60 秒后销毁;
  • 优势:响应快,适合短期、突发的小任务;
  • 坑:任务太多时可能创建大量线程,有 OOM 风险;
  • 代码:Executors.newCachedThreadPool()。

📢 王二的小试牛刀:用 CachedThreadPool 处理临时任务

比如处理一批 “用户登录日志”,任务小且执行快,用 CachedThreadPool 最合适:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorCachedDemo {
    public static void main(String[] args) {
        // 缓存型线程池:线程数随任务增减
        ExecutorService cachedPool = Executors.newCachedThreadPool();

        for (int i = 0; i < 20; i++) {
            int userId = i;
            cachedPool.submit(() -> {
                System.out.println("记录用户" + userId + "登录日志,线程:" + Thread.currentThread().getName());
            });
        }

        cachedPool.shutdown();
    }
}

3. SingleThreadExecutor(单线程线程池)

  • 特点:只有 1 个线程,任务按顺序执行;
  • 场景:需要 “串行执行” 的任务(比如数据库备份、日志归档);
  • 优势:不用自己加锁,天然线程安全;
  • 代码:Executors.newSingleThreadExecutor()。

哇哥的灵魂提问:为啥不用 new Thread () 做串行?

“比如你要按顺序备份 10 个数据库,用 SingleThreadExecutor 不用加锁,任务排队执行;用 new Thread () 的话,你得自己写代码控制顺序,麻烦还容易错。”

四、总结:Executor 框架的核心优势(王二记满 3 页笔记)

王二掏出小本本,把哇哥的话记成 “傻瓜式总结”:

  • 省钱:复用线程,减少创建销毁的开销,CPU 不用再 “打杂”;
  • 安全:控制线程数量,避免 OOM,服务器稳如老狗;
  • 省心:统一管理线程,任务提交、线程关闭全有规范,出问题好排查;
  • 灵活:现成线程池按需挑,不用自己造轮子。

彩蛋:哇哥的 “黑历史”

“我刚工作时,比你还虎,” 哇哥笑着说,“用 new Thread () 跑 2000 个任务,直接把测试环境搞挂了,领导让我把《Java 并发编程实战》抄了一遍 —— 现在我写并发代码,先想‘Executor 有没有现成的池’。”

下一篇预告:
王二用 FixedThreadPool 跑了两天,又遇到新问题:“任务堆积在队列里,用户投诉库存同步太慢!” 哇哥说这是 “线程池参数没配对”—— 下一篇我们扒透:

  • 线程池的核心参数(核心线程数、最大线程数、队列、拒绝策略);
  • 怎么自定义线程池,而不是只靠 Executors;
  • 定时任务线程池(ScheduledExecutorService)怎么用;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值