为啥 Set.add() 方法在匿名 Runnable 中不起作用?
Posted
技术标签:
【中文标题】为啥 Set.add() 方法在匿名 Runnable 中不起作用?【英文标题】:Why Set.add() method does not work in anonymous Runnable?为什么 Set.add() 方法在匿名 Runnable 中不起作用? 【发布时间】:2022-01-11 07:12:24 【问题描述】:我想同时运行 20 个线程来测试我的 id 生成器。但是, uniqueSet.add(id) 不会将 id 添加到集合中。当我断言时,它说集合大小为零。
@Test
void should_generate_unique_id_concurrently() throws InterruptedException
Set<Long> uniqueSet = ConcurrentHashMap.newKeySet();
final int numberOfThreads = 20;
ExecutorService service = Executors.newFixedThreadPool(numberOfThreads);
CountDownLatch latch = new CountDownLatch(numberOfThreads);
for (int i = 0; i < numberOfThreads; i++)
service.submit(() ->
try
latch.countDown();
latch.await();
long id = idGenerator.nextId();
uniqueSet.add(id);
catch (InterruptedException e)
e.printStackTrace();
);
assertEquals(numberOfThreads, uniqueSet.size());
【问题讨论】:
不确定这是否能解决问题,但测试至少应该等到所有提交的任务都已执行。这涉及收集由service.submt(...)
返回的所有Future<?>
s,然后是get()
ting 所有这些Future<?>
s 的值。
@Turing85 不需要期货。 service.shutdown
后跟 service.awaitTermination
将完成同样的事情。
@VGR 是的,这在这种特殊情况下也有效。
【参考方案1】:
uniqueSet.add(id)
可能工作得很好。但最有可能的是,在主线程询问集合的大小之前,这些任务中没有一个能够执行该行。
您的每个任务都调用latch.countDown()
之前它会执行其他任何操作。这意味着,在所有 20 个任务都启动之前,所有任务都无法执行任何其他操作。
与此同时,您的主线程在将最后一个任务提交到线程池后立即询问集合的大小。主线程此时已经在运行。可能大多数池线程都在latch.await()
中休眠,那些还没有走那么远的线程。主线程可能总是在任何工作线程到达uniqueSet.add(id)
行之前调用uniqueSet.size()
。
@VGR 为您的问题提出了一个很好的解决方案:在您的主线程提交了 20 个任务之后,在它检查集合的大小之前,它可以关闭线程池:
for (int i = 0; i < numberOfThreads; i++)
service.submit(...);
// Tell the `service` to refuse new tasks, and to shut down
// _after_ all of the pending tasks have completed.
service.shutdown();
// Wait until all of the tasks have completed.
service.awaitTermination();
assertEquals(numberOfThreads, uniqueSet.size());
【讨论】:
我按照你和@VGR提到的步骤,终于解决了问题。谢谢。以上是关于为啥 Set.add() 方法在匿名 Runnable 中不起作用?的主要内容,如果未能解决你的问题,请参考以下文章