You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java ThreadPool双队列实现邮局任务调度执行时机异常排查

问题根因
  • 生产任务、队列转移逻辑全部耦合在main单线程中串行执行,main线程全程无阻塞操作占用CPU调度资源,线程池的工作线程无法抢到时间片启动,只能等待main线程的插入循环全部结束后才开始执行,导致所有任务集中启动。
  • 现有线程池配置下,core线程数等于窗口数,前numSportelli个任务提交后会直接创建核心线程执行,不会进入线程池内置的第二个队列,不符合「任务必须先进入第二个队列再执行」的设计要求。
修复方案
  1. 拆分队列转移逻辑到独立线程,和生产任务逻辑并行运行,避免main线程独占CPU
  2. 调整线程池参数,强制所有提交的任务优先进入内置队列,满足双队列流转要求
  3. 优化队列监听逻辑,避免空轮询占用资源
修复后完整代码
import java.util.concurrent.*;
import java.util.*;

class Task implements Runnable{
    public int id;
    public Task(int id){
        this.id=id;
    }
    public void run(){
        int delay = ThreadLocalRandom.current().nextInt(0, 50 + 1);
        try {
            Thread.sleep(delay);
            System.out.printf("Persona %d: eseguita\n",this.id);
        }  
        catch (InterruptedException e) {
            System.err.println("Interruzione su sleep.");
        }
        System.out.printf("Persona %d: uscita da sportello\n", this.id);
    }
}

class UfficioPostale{
    private ThreadPoolExecutor pool;
    private ArrayBlockingQueue<Runnable> q;

    public UfficioPostale(int numSportelli,int dimq){
        q = new ArrayBlockingQueue<>(dimq);
        // 调整线程池配置:coreSize设为0,允许核心线程超时,所有任务优先进队列
        pool = new ThreadPoolExecutor(0,numSportelli,60L,TimeUnit.MILLISECONDS,q);
        pool.allowCoreThreadTimeOut(true);
    }

    public void executeTask(Task t){
        pool.execute(t);
    }
    public int sizeq(){
        return q.size();
    }
    public void close(){
        pool.shutdown();
    }
}

public class First{
    public static void main(String[] args) throws InterruptedException {
        int numSportelli = 4; // 窗口数量
        int dimq = 10; // 第二个队列(线程池内置队列)大小
        int k = 50; // 第一个队列大小
        int taskCount = 50; // 总任务数
        LinkedBlockingQueue<Task> q1 = new LinkedBlockingQueue<>(k);
        UfficioPostale p = new UfficioPostale(numSportelli, dimq);

        // 启动独立的队列转移线程:持续将q1的任务转移到线程池的第二个队列
        Thread transferThread = new Thread(() -> {
            int transferred = 0;
            while (transferred < taskCount) {
                try {
                    // 只要第二个队列有空位,就从q1取任务提交
                    if (p.sizeq() < 10) {
                        Task task = q1.take();
                        p.executeTask(task);
                        transferred++;
                    } else {
                        // 第二个队列满了,休眠1ms避免空轮询
                        Thread.sleep(1);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
        transferThread.start();

        // main线程只负责生产任务插入第一个队列
        for (int i = 1; i <= taskCount; i++) {
            Task t = new Task(i);
            q1.put(t);
            System.out.println("Task "+i+" inserted in queue 1");
            // 加短休眠模拟真实业务中任务生成的间隔,也让出CPU给调度器
            Thread.sleep(1);
        }

        // 等待所有任务转移完成
        transferThread.join();
        // 关闭线程池
        p.close();
    }
}
效果说明

修复后运行代码可以看到,任务插入第一个队列的同时,就有任务开始执行输出,符合边排队边处理的预期,且所有任务都会先经过第一个队列、再进入第二个队列后才会被调度执行。

内容的提问来源于stack exchange,提问作者Dylan Nico

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 07:18:03