关于ExecutorService#submit的疑问:为何无法按顺序输出1到10?
嘿,这个问题我之前也踩过坑,咱们来一步步分析为什么实际输出和你的预期不一样:
核心原因
1. 线程任务的执行是无序的
你用的Executors.newCachedThreadPool()是一个可缓存的线程池,它会根据任务数量创建多个线程并行执行提交的Callable任务。这些任务的执行顺序完全由CPU调度决定,不是按你提交的先后顺序完成的。比如第3个提交的任务可能比第1个任务更早完成Thread.sleep(20)和自增操作。
2. 共享的AtomicInteger导致返回值不是任务专属的结果
你所有的Callable都返回同一个全局的AtomicInteger对象ai。当某个任务执行完ai.incrementAndGet()后,还没来得及返回结果,其他任务可能已经对ai进行了多次自增。比如任务1刚把ai从0变成1,还没返回,任务2、3已经把ai变成3了,这时候任务1返回的ai的当前值就是3,而不是你期望的1。
3. 提前关闭线程池的小问题
你在遍历Future列表的finally块里调用了es.shutdown(),这会导致第一次循环时就关闭了线程池,后续的任务可能会被中断或者无法正常执行,不过这不是导致顺序问题的核心原因,但也是需要修正的点。
解决办法
如果你希望控制台按顺序输出1 2 3 ... 10,可以根据你的需求选择以下方案:
方案1:让任务串行执行(简单但失去并行优势)
把线程池换成单线程的Executors.newSingleThreadExecutor(),这样任务会按提交顺序依次执行,每个任务自增后返回的就是当前的顺序值:
ExecutorService es = Executors.newSingleThreadExecutor();
方案2:让每个任务返回自己的自增结果(保留并行,收集后可排序)
修改Callable,让它返回自增后的具体数值,而不是共享的AtomicInteger对象,这样每个任务的结果都是自己操作后的那个值。如果需要按顺序输出,可以收集所有结果后排序:
list.add(es.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { try { Thread.sleep(20); return ai.incrementAndGet(); // 返回自增后的具体数值 } catch (InterruptedException e) { e.printStackTrace(); return -1; } } })); // 收集结果后排序输出 List<Integer> results = new ArrayList<>(); for (Future<Integer> future : list) { results.add(future.get()); } Collections.sort(results); results.forEach(System.out::println);
方案3:使用CountDownLatch等待所有任务完成,输出最终值(如果只需要最终结果)
如果你的需求只是确认最终ai的值是10,可以用CountDownLatch等待所有任务完成后再输出:
private static final int ii = 10; public static AtomicInteger ai = new AtomicInteger(); public static CountDownLatch latch = new CountDownLatch(ii); public static void main(String[] args) throws InterruptedException { ExecutorService es = Executors.newCachedThreadPool(); for (int i = 0; i < ii; i++) { es.submit(new Callable<Void>() { @Override public Void call() throws Exception { try { Thread.sleep(20); ai.incrementAndGet(); } catch (InterruptedException e) { e.printStackTrace(); } finally { latch.countDown(); } return null; } }); } latch.await(); // 等待所有任务完成 System.out.println(ai.get()); // 输出10 es.shutdown(); }
内容的提问来源于stack exchange,提问作者Nugats

