【问题标题】:ExecutorService inconsistencyExecutorService 不一致
【发布时间】:2020-06-24 05:22:27
【问题描述】:

我正在测试HashIds 的碰撞。这是依赖关系:

    <!-- https://mvnrepository.com/artifact/org.hashids/hashids -->
    <dependency>
        <groupId>org.hashids</groupId>
        <artifactId>hashids</artifactId>
        <version>1.0.3</version>
    </dependency>

我正在使用以下代码:

    Hashids hashids = new Hashids("xyz", 6, "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ");

    System.out.println("*******************************************************************");
    System.out.println("Started");

    Set<String> set = new HashSet();

    ExecutorService executor = Executors.newFixedThreadPool(4);

    AtomicLong count = new AtomicLong(0);
    final long max = 10_000_000;
    for (long i = 1; i <= max; i++) {

        executor.execute(() -> {
            set.add(hashids.encode(count.incrementAndGet()));

            // This is just to show me that there is some activity going on
            if (count.get() % 100_000 == 0) {
                System.out.println(count.get() + ": " + new Date());
            }
        });
    }

    // Wait till the executor service tasks are done 
    executor.shutdown();
    while (!executor.isTerminated()) {
        Thread.sleep(1000);
    }

    // Assert that the Set did not receive duplicates
    Assert.assertEquals(max, set.size());

    System.out.println("Ended");
    System.out.println("*******************************************************************");

所以,我放了一个ExecutorService 让它更快一点,但我遇到了问题。要么

  1. ExecutorService 未完成并挂在那里
  2. Set 包含重复值,因此断言失败
  3. 使用裸for 循环比使用ExecutorService 快得多

这段代码有什么问题?

【问题讨论】:

    标签: java multithreading


    【解决方案1】:
    1. 您需要在executor.shutdown() ; 之后立即致电executor.awaitTermination
    2. 您的Set 不是线程安全的,请尝试使用Collections.synchronizedSet(set) 包装您的集合,或基于ConcurrentHashMap.newKeySet() 创建集合,请参阅有关thread safe set 的讨论。

    【讨论】:

    • ConcurrentHashMap.newKeySet()synchronizedSet 更容易使用,因为您不需要手动处理同步。
    • 我忘记了Collections.synchronizedSet(set)。现在已经奏效了。虽然点号3 仍然悬而未决。没有ExecutorService,与使用ExecutorService相比,花费的时间更少
    • 是的,ConcurrentHashMap.newKeySet() 的并发性能可能比synchronizedSet 更好,我会更新答案。
    • @Kihats 第 3 点可能是创建不同线程、启动和加入它们的开销大于您从中获得的时间收益
    • Collections.synchronizedSet(set) 只是添加同步块,因此可能会增加set.add() 的时间。另一种分析方法,您可以尝试将在 hashids.encode() 上花费的时间与另一个 AtomicLong 变量相加。
    【解决方案2】:

    将大量相对较快的任务排队可能会产生大量开销。相反,您只能排队 4 个任务,这些任务循环遍历整数的一个分区。由于减少了排队和可能的代码局部性(缓存)的开销,这将快得多。此外,它还避免了对计数器和集合的并发访问。

    【讨论】:

      猜你喜欢
      • 2016-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-23
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多