线程池中任务堆积与饥饿死锁问题

一、Bug 场景

在一个电商后台系统中,使用线程池来处理订单处理、库存更新、物流通知等多种异步任务。随着业务量的增长,系统逐渐出现响应变慢的情况,部分任务甚至长时间得不到执行,最终导致一些关键业务流程受阻。

二、代码示例

任务类

java 复制代码
public class BusinessTask implements Runnable {
    private final String taskName;

    public BusinessTask(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        try {
            // 模拟业务处理
            Thread.sleep(1000);
            System.out.println(taskName + " 任务完成");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

线程池配置与任务提交(有缺陷)

java 复制代码
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class EcommerceSystem {
    private static final int CORE_POOL_SIZE = 5;
    private static final int MAX_POOL_SIZE = 10;
    private static final int QUEUE_CAPACITY = 20;
    private static final long KEEP_ALIVE_TIME = 1L;

    public static void main(String[] args) {
        BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>(QUEUE_CAPACITY);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                taskQueue);

        // 模拟提交大量任务
        for (int i = 0; i < 50; i++) {
            String taskName = "任务 " + i;
            executor.submit(new BusinessTask(taskName));
        }

        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
                if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                    System.err.println("线程池未正常关闭");
                }
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

三、问题描述

  1. 预期行为:线程池能够高效处理提交的任务,所有任务都能在合理时间内完成,系统保持稳定运行。

  2. 实际行为

    • 任务堆积:随着任务不断提交,任务队列逐渐被填满,新任务只能等待队列有空位或者线程池创建新线程(达到最大线程数后也只能等待)。这是因为线程池的核心线程数和最大线程数设置相对业务量过小,并且任务执行时间较长,导致任务处理速度跟不上提交速度。
    • 饥饿死锁:在任务队列中可能存在优先级不同的任务,例如订单处理任务优先级较高,物流通知任务优先级较低。如果高优先级任务持续提交,线程池中的线程会一直忙于处理高优先级任务,低优先级任务可能长时间得不到执行,出现饥饿现象。极端情况下,可能导致低优先级任务永远无法执行,形成一种类似死锁的状态。

四、解决方案

  1. 优化线程池参数:根据实际业务负载和任务特性,合理调整线程池参数。例如,如果任务执行时间较短且数量较多,可以适当增加核心线程数和最大线程数;如果任务执行时间较长,可以扩大任务队列容量。
java 复制代码
private static final int CORE_POOL_SIZE = 10;
private static final int MAX_POOL_SIZE = 20;
private static final int QUEUE_CAPACITY = 50;
  1. 使用优先级队列和优先级线程池 :对任务进行优先级划分,使用 PriorityBlockingQueue 作为任务队列,并自定义 PriorityThreadPoolExecutor 来处理不同优先级的任务。
java 复制代码
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class PriorityBusinessTask implements Runnable, Comparable<PriorityBusinessTask> {
    private final String taskName;
    private final int priority;

    public PriorityBusinessTask(String taskName, int priority) {
        this.taskName = taskName;
        this.priority = priority;
    }

    @Override
    public void run() {
        try {
            // 模拟业务处理
            Thread.sleep(1000);
            System.out.println(taskName + " 任务完成");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    @Override
    public int compareTo(PriorityBusinessTask other) {
        return Integer.compare(other.priority, this.priority);
    }
}

public class PriorityThreadPoolExecutor extends ThreadPoolExecutor {
    public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        if (r instanceof PriorityBusinessTask) {
            System.out.println("开始执行优先级任务: " + ((PriorityBusinessTask) r).taskName);
        }
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (r instanceof PriorityBusinessTask) {
            System.out.println("完成优先级任务: " + ((PriorityBusinessTask) r).taskName);
        }
    }
}

public class EcommerceSystem {
    private static final int CORE_POOL_SIZE = 10;
    private static final int MAX_POOL_SIZE = 20;
    private static final int QUEUE_CAPACITY = 50;
    private static final long KEEP_ALIVE_TIME = 1L;

    public static void main(String[] args) {
        BlockingQueue<Runnable> taskQueue = new PriorityBlockingQueue<>(QUEUE_CAPACITY);
        PriorityThreadPoolExecutor executor = new PriorityThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                taskQueue);

        // 模拟提交不同优先级任务
        executor.submit(new PriorityBusinessTask("高优先级任务 1", 1));
        executor.submit(new PriorityBusinessTask("低优先级任务 1", 3));
        executor.submit(new PriorityBusinessTask("高优先级任务 2", 1));
        executor.submit(new PriorityBusinessTask("低优先级任务 2", 3));

        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
                if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                    System.err.println("线程池未正常关闭");
                }
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}
  1. 动态调整线程池:根据系统运行时的任务队列长度、线程池活跃线程数等指标,动态调整线程池的参数,以适应业务量的变化。可以使用定时任务定期检查并调整线程池大小。
java 复制代码
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class EcommerceSystem {
    private static final int INITIAL_CORE_POOL_SIZE = 5;
    private static final int INITIAL_MAX_POOL_SIZE = 10;
    private static final int QUEUE_CAPACITY = 20;
    private static final long KEEP_ALIVE_TIME = 1L;

    public static void main(String[] args) {
        BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>(QUEUE_CAPACITY);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                INITIAL_CORE_POOL_SIZE,
                INITIAL_MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                taskQueue);

        ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
        scheduler.scheduleAtFixedRate(() -> {
            int queueSize = taskQueue.size();
            int activeCount = executor.getActiveCount();
            if (queueSize > QUEUE_CAPACITY * 0.8 && activeCount < INITIAL_MAX_POOL_SIZE) {
                executor.setCorePoolSize(executor.getCorePoolSize() + 1);
                executor.setMaximumPoolSize(executor.getMaximumPoolSize() + 1);
            } else if (queueSize < QUEUE_CAPACITY * 0.2 && activeCount > INITIAL_CORE_POOL_SIZE) {
                executor.setCorePoolSize(executor.getCorePoolSize() - 1);
                executor.setMaximumPoolSize(executor.getMaximumPoolSize() - 1);
            }
        }, 0, 1, TimeUnit.MINUTES);

        // 模拟提交大量任务
        for (int i = 0; i < 50; i++) {
            String taskName = "任务 " + i;
            executor.submit(new BusinessTask(taskName));
        }

        executor.shutdown();
        scheduler.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
                if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                    System.err.println("线程池未正常关闭");
                }
            }
            if (!scheduler.awaitTermination(1, TimeUnit.MINUTES)) {
                scheduler.shutdownNow();
                if (!scheduler.awaitTermination(1, TimeUnit.MINUTES)) {
                    System.err.println("调度器未正常关闭");
                }
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            scheduler.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}
相关推荐
deng-c-f6 小时前
C/C++内置库函数(2):智能指针
java·c语言·c++
毕设源码-朱学姐6 小时前
【开题答辩全过程】以 基于SSM框架的餐厅点餐系统的设计与实现为例,包含答辩的问题和答案
java·eclipse
pursue.dreams6 小时前
Java实现企业微信机器人消息推送:文本消息与文件推送完整指南
java·机器人·企业微信
狂奔小菜鸡6 小时前
Day30 | Java集合框架之Collections工具类
java·后端·java ee
Java天梯之路6 小时前
Spring Boot 钩子全集实战(二):`SpringApplicationRunListener.starting()` 详解
java·spring·面试
忘带键盘了6 小时前
eclipse配置
java·ide·eclipse
Aevget6 小时前
知名Java开发工具IntelliJ IDEA v2025.3正式上线——开发效率全面提升
java·ide·人工智能·intellij-idea·开发工具
没有bug.的程序员7 小时前
JVM 安全与沙箱深度解析
java·jvm·安全·gc·gc调优
程序媛青青7 小时前
Java 中 NIO 和IO 的区别
java·开发语言·nio