Java并发测试中匿名Runnable调用Set.add()不生效是什么原因?
并发ID生成器唯一性测试集合为空问题解决方案
问题根因
你的代码仅实现了让20个工作线程同时触发ID生成的起跑同步逻辑,未做主线程等待所有工作线程完成ID写入集合的结束同步:提交完任务后主线程会立即执行断言,此时工作线程大概率还未执行到uniqueSet.add(id)逻辑,因此集合大小为0。
解决方案
方案1:新增结束同步锁存器(推荐,保留原有线程同时起跑的压测逻辑)
你当前的CountDownLatch仅用于让所有线程同时开始执行,新增一个结束用的锁存器等待所有线程完成写入即可:
@Test void should_generate_unique_id_concurrently() throws InterruptedException { Set<Long> uniqueSet = ConcurrentHashMap.newKeySet(); final int numberOfThreads = 20; ExecutorService service = Executors.newFixedThreadPool(numberOfThreads); // 原有起跑同步锁存器:让所有线程同时开始生成ID CountDownLatch startLatch = new CountDownLatch(numberOfThreads); // 新增结束同步锁存器:等待所有线程完成ID写入 CountDownLatch endLatch = new CountDownLatch(numberOfThreads); for (int i = 0; i < numberOfThreads; i++) { service.submit(() -> { try { startLatch.countDown(); startLatch.await(); long id = idGenerator.nextId(); uniqueSet.add(id); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } finally { // 无论是否异常都计数,避免主线程永久阻塞 endLatch.countDown(); } }); } // 主线程等待所有工作线程完成写入后再执行断言 endLatch.await(); service.shutdown(); assertEquals(numberOfThreads, uniqueSet.size()); }
方案2:直接等待线程池所有任务执行完成
如果不需要严格控制所有线程同时起跑,可以直接关闭线程池后等待任务结束:
@Test void should_generate_unique_id_concurrently() throws InterruptedException { Set<Long> uniqueSet = ConcurrentHashMap.newKeySet(); final int numberOfThreads = 20; ExecutorService service = Executors.newFixedThreadPool(numberOfThreads); for (int i = 0; i < numberOfThreads; i++) { service.submit(() -> { long id = idGenerator.nextId(); uniqueSet.add(id); }); } service.shutdown(); // 等待最长1分钟所有任务执行完成 service.awaitTermination(1, TimeUnit.MINUTES); assertEquals(numberOfThreads, uniqueSet.size()); }
补充注意事项
- 捕获
InterruptedException后建议手动恢复线程中断状态,避免吞掉中断信号 - 如果修改后断言还是失败,说明
idGenerator本身存在重复生成ID的问题,需要进一步排查ID生成逻辑
内容的提问来源于stack exchange,提问作者Aksoy
相关产品推荐
相关产品推荐

