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

