为啥 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&lt;?&gt;s,然后是get()ting 所有这些Future&lt;?&gt;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 中不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥局部函数生成的 IL 不同于匿名方法和 Lambda 表达式?

为啥在匿名类中只能访问最终变量?

为啥 Flux.zip 接受预定义函数而不接受匿名函数?

Java 基础知识点 笔记总结

set&map的遍历方法

为啥以及如何在 PHP 中使用匿名函数?