Java线程池

一、为什么需要线程池?

有了线程,为什么还需要线程池?线程池的作用是什么?

线程池是 Java 并发编程中非常重要的一部分,用来 管理和复用线程,提高程序性能

如果每次任务都创建线程都需要:new Thread().start();

会带来问题:1. 线程创建和销毁开销大 2.大量线程会占用系统资源 3. 可能导致系统崩溃

假设有10000个任务 每个任务都创建线程 记得10000个线程 CPU会被拖垮。

线程池,就是为了让我们高效的创建销毁线程的

最初引入线程的原因:频繁创建销毁进程,太慢了

随着互联网的发展,随着我们对于性能要求更进一步咱们现在觉得,频繁创建销毁线程,开销有些不能接受了

线程池最大的好处就是减少每次启动、销毁线程的损耗。

二、标准库中的线程池

Java标准库里也提供了直接使用的线程池 ThreadPoolExecutor构造这个类的时候,构造方法,比较麻烦.(参数有点多)

理解ThreadPoolExecutor构造方法的参数

  • corePoolSize:核心线程数 。至少有多少个线程线程池一创建,这些线程也要随之创建直到整个线程池销毁,这些线程才会销毁。正式员工的数量.(正式员工,一旦录用,永不辞退)
  • maximumPoolSize:最大线程数 核心线程+非核心线程(不繁忙就销毁繁忙就再创建) 正式员工+临时工的数目.(临时工:一段时间不干活,就被辞退).
  • keepAliveTime:非核心线程允许空闲的最大时间 允许实习生摸鱼的时间如果实习生连续一个月都没有啥活了就可以考虑优化掉
  • unit:keepaliveTime的时间单位,是秒,分钟,还是其他值
  • BlockingQueue<Runnable> workQueue:工作队列 线程池,本质上也是生产者消费者模型.调用submit就是在生产任务线程池里的线程就是在消费任务
  • threadFactory:工厂模式(也是一种设计模式,和单例模式是并列的关系)用来弥补构造方法的缺陷的
  • RejectedExecutionHandler:拒绝策略,当线程池达到最大线程数并且任务队列已满时,新的任务无法处理,这时就会执行拒绝策略

三、使用 Executors 创建常见线程池

Java 提供了 Executors 工具类来快速创建线程池。

Executors创建线程池的几种方式

1.newFixedThreadPool:创建固定线程数的线程池

java 复制代码
public class Demo {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(4);//最大线程数
        for (int i = 0; i < 1000; i++) {
            int id = i;
            //核⼼操作为submit,将任务加⼊线程池中
            threadPool.submit(() ->{
                System.out.println("this is " + id +", "+ Thread.currentThread().getName());
            });
        }
    }
}

核心操作为submit,将任务加入线程池中

线程池中4个线程 完成1000个任务

2.newCachedThreadPool:创建线程数目动态增长的线程池.

java 复制代码
public class Demo {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 1000; i++) {
            int id = i;
            //核⼼操作为submit,将任务加⼊线程池中
            threadPool.submit(() ->{
                System.out.println("this is " + id +", "+ Thread.currentThread().getName());
            });
        }
    }
}

特点:线程数量不固定、空闲线程会被复用、适合执行大量短时间任务

3.newSingleThreadExecutor:创建只包含单个线程的线程池.

java 复制代码
public class Demo36 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            int id = i;
            //核⼼操作为submit,将任务加⼊线程池中
            threadPool.submit(() ->{
                System.out.println( Thread.currentThread().getName()+"执行任务 " + id);
            });
        }
    }
}

4.newScheduledThreadPool:设定延迟时间后执行命令,或者定期执行命令.是进阶版的Timer**.**

java 复制代码
//Timer
public class Demo {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("this is 3000");
            }
            //延迟时间
        },3000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("this is 2000");
            }
        },2000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("this is 1000");
            }
        },1000);
        System.out.println("this is main");
    }
}
java 复制代码
public class Demo {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);
        //任务1:延迟执行
        pool.schedule(() ->{
            System.out.println("任务1:延迟3秒执行");
        },3, TimeUnit.SECONDS);
        // 任务2:周期执行
        pool.scheduleAtFixedRate(() -> {
            System.out.println(Thread.currentThread().getName() + " 正在执行周期任务");
        },1,2,TimeUnit.SECONDS);
    }
}

schedule()延迟执行 scheduleAtFixedRate()周期执行。

Executors本质上是ThreadPoolExecutor类的封装.

四、工厂模式

工厂模式(Factory Pattern) 是一种常见的设计模式,属于创建型设计模式。它的核心思想是:

将对象的创建过程封装起来,由工厂负责创建对象,而不是由使用者直接 new 对象。

这样可以让代码 降低耦合度、提高扩展性

1.为什么需要工厂模式

没有工厂模式情况下

java 复制代码
Dog dog = new Dog();
Cat cat = new Cat();

如果对象有很多:鼠 牛 虎 兔 马.......

客户端代码就需要一直new新的对象 就会导致代码创建逻辑分散、耦合严重、扩展困难

往后再次新增类的话很多地方的代码就可能需要修改。所以为了解决这类问题 就引入了工厂模式

2.核心思想

  • 不直接new对象
  • 交给工厂类创建对象

好处:

  1. 解耦:创建和使用分开
  2. 易扩展:加新类不用改老代码
  3. 统一管控:对象创建逻辑集中管理
java 复制代码
// 1. 定义Phone接口(核心抽象层)
interface Phone {
    void makeCall(); // 声明打电话方法
}

// 2. 实现华为手机类
class HuaweiPhone implements Phone {
    @Override
    public void makeCall() {
        System.out.println("用华为手机打电话");
    }
}

// 3. 实现小米手机类
class XiaomiPhone implements Phone {
    @Override
    public void makeCall() {
        System.out.println("用小米手机打电话");
    }
}

// 4. 简单工厂类:根据参数生产不同手机
class SimplePhoneFactory {
    // 静态方法:不用创建工厂对象就能调用
    public static Phone createPhone(String type) {
        // 统一封装创建逻辑
        if ("huawei".equals(type)) {
            return new HuaweiPhone();
        } else if ("xiaomi".equals(type)) {
            return new XiaomiPhone();
        } else {
            throw new IllegalArgumentException("不支持的手机类型");
        }
    }
}


public class Demo39 {
    public static void main(String[] args) {
        // 调用方只需要传参数,不用自己 new
        Phone huawei = SimplePhoneFactory.createPhone("huawei");
        huawei.makeCall(); // 输出:用华为手机打电话

        Phone xiaomi = SimplePhoneFactory.createPhone("xiaomi");
        xiaomi.makeCall(); // 输出:用小米手机打电话
    }
}

五、模拟实现线程池

java 复制代码
//实现一个固定线程池个数的线程池
class MyThreadPool{
    private BlockingQueue<Runnable> queue = null;
    public MyThreadPool(int n){
        //初始化线程池 固定个数的线程
        queue = new ArrayBlockingQueue<>(1000);

        //创建n个线程
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(() -> {
                try{
                    while (true){
                        Runnable task = queue.take();
                        task.run();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
            t.start();
        }
    }
    public void submit(Runnable task) throws InterruptedException {
       //将任务放入队列中
        queue.put(task);
    }
}
public class Demo40 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool = new MyThreadPool(10);
        //向线程池提交任务
        for (int i = 0; i < 100; i++) {
            int num = i;
            pool.submit(() ->{
                System.out.println(Thread.currentThread().getName()+" num="+num);
            });
        }
    }
}
相关推荐
Mr_Xuhhh13 小时前
深入理解Java抽象类与接口:从概念到实战
java·开发语言
萝卜白菜。13 小时前
TongWeb7.0相同的类指明加载顺序
开发语言·python·pycharm
wb0430720113 小时前
使用 Java 开发 MCP 服务并发布到 Maven 中央仓库完整指南
java·开发语言·spring boot·ai·maven
Rsun0455113 小时前
设计模式应该怎么学
java·开发语言·设计模式
良木生香13 小时前
【C++初阶】:C++类和对象(下):构造函数promax & 类型转换 & static & 友元 & 内部类 & 匿名对象 & 超级优化
c语言·开发语言·c++
5系暗夜孤魂14 小时前
系统越复杂,越需要“边界感”:从 Java 体系理解大型工程的可维护性本质
java·开发语言
二月夜14 小时前
Spring循环依赖深度解析:从三级缓存原理到跨环境“灵异”现象
java·spring
无巧不成书021814 小时前
C语言零基础速通指南 | 1小时从入门到跑通完整项目
c语言·开发语言·编程实战·c语言入门·零基础编程·c语言速通
nbwenren14 小时前
Springboot中SLF4J详解
java·spring boot·后端
三雷科技14 小时前
使用 `dlopen` 动态加载 `.so` 文件
开发语言·c++·算法