线程池-抢票系统性能优化

文章目录

引言-购票系统

java 复制代码
public class App implements Runnable {
    private static int tickets = 100;
    private static int users = 10000;

    private final ReentrantLock lock = new ReentrantLock(true);

    public void run() {
        try {
            lock.lock();
            reduceTickets(); // 减票
        } finally {
            lock.unlock();
        }
    }

    private void reduceTickets() {
        if (tickets <= 0) return;
        System.out.println(Thread.currentThread().getName() + "抢到第" + tickets-- + "票");
    }

    private static void initTask(){
        App task = new App();
        Thread[] threads = new Thread[users];
        for (int i = 0; i < users; i++) {
            threads[i] = new Thread(task, "用户" + i);
            threads[i].start();
        }

        // 等待所有线程执行完毕
        for (Thread thread : threads) {
            try {
                thread.join();   // 让主线程等待该子线程执行完成后再继续
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();

        // 初始化任务
        initTask();

        long endTime = System.currentTimeMillis();
        System.out.println("程序执行时间: " + (endTime - startTime) + " 毫秒");
    }
}

在前面,我们通过创建多个线程模拟抢票场景,并且使用加锁的方式解决了车票超卖的问题。

每次创建一个线程,都需要执行如下步骤:

  1. 手动创建一个线程对象
  2. 执行任务
  3. 执行完毕,释放线程对象

‼️当用户量较大时,就需要频繁的创建线程对象、释放线程对象,十分麻烦。

解决方案,则是引入线程池

  1. 线程复用:在线程池中初始化指定数量的核心线程数,用户需要时直接使用线程,不需要重新创建一个新的线程对象,实现对象的复用。
  2. 线程回收:执行完任务之后,线程不会销毁,而是放回线程池中继续等待使用。
  3. 提高系统的响应速度,线程的利用率。

线程池

Java线程池是一种用于优化线程使用和管理的工具,它通过复用一定数量的线程来执行多个任务,从而减少了创建和销毁线程的开销,提高了程序的性能和响应速度。

Java中的线程池是通过java.util.concurrent包下的Executor接口及其子类来实现的。

以下是一些关键的类和接口,用于在Java中创建和使用线程池:

  1. Executor接口:这是一个基础接口,用于执行提交的任务。
  2. ExecutorService接口:扩展了Executor接口,添加了用于管理执行器生命周期的方法,如关闭线程池。
  3. ThreadPoolExecutor类:是ExecutorService的一个实现,它允许更详细地配置线程池。
  4. Executors类:提供了创建线程池的工厂方法。

以下是几种常见的线程池类型,可以通过Executors类来创建:

  1. FixedThreadPool:固定数量的线程池,适用于负载比较重的服务器。
  2. SingleThreadExecutor:只有一个线程的线程池,适用于需要保证顺序执行的场景。
  3. CachedThreadPool:根据需要创建新线程的线程池,适用于执行很多短期异步任务的程序。
  4. ScheduledThreadPool:用于执行定时任务或周期性任务。

购票系统-线程池优化

  1. 初始化:线程池将根据预设配置初始化一定数量的核心线程。
  2. 新增线程:当任务提交量超过核心线程数时,系统会按需创建额外的工作线程以处理新增的任务负载。
  3. 最大线程数:若当前活跃的线程数目已达到设定的最大值,新到达的任务会被安排进入等待队列中暂存。
  4. 拒绝策略:在等待队列也达到了其容量上限的情况下,对于后续继续提交的新任务,线程池将依据预定义的拒绝策略进行处理,即不再接受新的任务请求。
java 复制代码
public class ThreadPool implements Runnable {
    private static int tickets = 100;
    private static int users = 200000;
    private final ReentrantLock lock = new ReentrantLock(true);

    public void run() {
        try {
            lock.lock();
            reduceTickets(); // 减票
        } finally {
            lock.unlock();
        }
    }

    private void reduceTickets() {
        if (tickets <= 0) return;
        System.out.println(Thread.currentThread().getName() + "抢到第" + tickets-- + "票");
    }

    private static void initTask(){
        // 创建线程池
        ExecutorService pool = new ThreadPoolExecutor(
                5,    // 核心线程数
                16,   // 最大线程数
                0L, TimeUnit.MILLISECONDS,  // 线程空闲时间
                new LinkedBlockingQueue<Runnable>() // 任务队列
        );

        // 创建任务
        ThreadPool task = new ThreadPool();
        for (int i = 0; i < users; i++) {
            pool.submit(new Thread(task, "用户" + i));
        }

        // 执行完毕,关闭线程池
        pool.shutdown();
        try {
            // 等待所有线程执行完毕
            if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
                pool.shutdownNow(); // 超时强制关闭
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();

        // 初始化任务
        initTask();

        long endTime = System.currentTimeMillis();
        System.out.println("程序执行时间: " + (endTime - startTime) + " 毫秒");
    }
}

池化 vs 未池化

  1. 电脑核心线程数( NumberOfCores ) = 12,逻辑核心数 = 16
  2. 票数 = 100,用户数量 = 10000,模拟 10000 个用户同事抢 100 张票。
**** 核心线程数 最大线程数 执行时间
未池化 18832 ms
线程池配置 0 5 8 54 ms
线程池配置 1 5 10 56 ms
线程池配置 2 5 16 27 ms

池化后,性能得到了质的飞跃🚀。

相关推荐
疏狂难除1 分钟前
JetBrains IDE插件开发教程(四)——Action
java·ide·kotlin
laufing5 分钟前
java web 基础 ---- servlet
java·servlet·web开发
程序猿乐锅8 分钟前
【苍穹外卖|Day01】项目初识:从多模块结构到 OpenAPI 接口文档踩坑
java·spring·maven·mybatis
jasnet_u9 分钟前
SpringCloud中服务集成PlumeLog日志系统
spring·spring cloud·plumelog·日志收集
李白的天不白10 分钟前
针对你遇到的 Client.Timeout exceeded 问题,我判断是防火墙拦截了 HTTPS 流量
java
linweidong14 分钟前
Java 后端开发面试 50 个高频易混淆知识点详解
java·spring boot·spring·spring cloud·面试·mybatis·spring事务
码语智行14 分钟前
应用启动和关闭监听器功能分析
java·spring boot
Resky081815 分钟前
什么是 Spring IOC:倒过来让容器帮你 new,而不是你到处 new
java·spring
AutumnWind042015 分钟前
【JDK动态代理源码梳理】
java·后端·spring