【问题标题】:asynchronous for loop no hanging异步for循环不挂
【发布时间】:2018-09-15 19:51:54
【问题描述】:

我写了一个这样的同步for循环:

for (Map.Entry<String, CustomClass> entry : assocs.entrySet()) {
  String key = entry.getKey();
  CustomClass value = entry.getValue();
  value.tick(key);
}

问题是有时(很少).tick 挂起。如果是单个.tick,没什么大不了的,它会在一段时间后自行修复,实际上根本不重要(这是由于客户端的互联网速度较慢)。但如果它耽误了其他人,那就是个问题了。

所以我希望每个循环体都运行而不等待其他循环体完成。

【问题讨论】:

  • 记住:除非tick需要很长时间,否则线程会减慢你的速度。不是想劝阻你,只是说线程并不总是答案,你应该确保测量结果的性能(之前和之后)。
  • 减轻压力,使用流。
  • @Taslim 我从来没有听说过流,它是如何工作的?
  • @BillK 我不一定需要线程它(或者我?),问题是有时(很少).tick 挂起并导致整个应用程序出现问题。如果单个.tick 挂起没问题,一段时间后它会自行修复,但如果它延迟其​​余的,那就是问题
  • 如果您希望一个启动而另一个仍在运行,那么您需要一个线程。您需要注意,“tick”中的所有代码都必须是线程安全的。使用并行流方法,您将一次触发许多滴答声,我认为这不是您想要的(根据“滴答声”一词的性质猜测)。

标签: java multithreading loops asynchronous


【解决方案1】:

Andrew's solution 几乎是正确的,因为它使用了一个包含 10 个线程的固定线程池;如果assocs 中包含的元素超过 10 个,则第十一个元素可能会停止,因为它将与其余元素竞争线程。正确的方法是使用带有缓存线程池的ExecutorService

ExecutorService service = Executors.newCachedThreadPool();

assocs.entrySet().forEach(entry -> {
    service.execute(() -> {
        String key = entry.getKey();
        CustomClass value = entry.getValue();
        value.tick(key);
    );
);

【讨论】:

  • 我的实际 for 循环体比我在我的问题中提出的要大,而且我以前在 Java 中没有见过这个 -&gt; 表示法。我可以在箭头后面加上{ 来运行我的所有语句吗?
  • 我明白了,是的!我已经编辑了答案,以向您展示它的外观。
  • @theonlygusti -&gt; 语法是 Lambda Expression
【解决方案2】:

可能最简单的并行化方法是使用并行流:

 assocs.entrySet().parallelStream()
     .forEach(e -> e.getValue().tick(e.getKey()));

但请注意,这将使用ForkJoinPool.commonPool 来执行您的线程,该线程比您拥有的处理器少一个。

如果你想增加并行度,你总是可以在自己的 ForkJoinPool 中运行

new ForkJoinPool(numberOfThreads).submit(() ->
    assocs.entrySet().parallelStream()
        .forEach(e -> e.getValue().tick(e.getKey())));

【讨论】:

  • 我需要使用Final 还是什么?
【解决方案3】:

说实话,创建 ExecutorService 比手动管理自己的线程要简单:

final ExecutorService exec = Executors.newFixedThreadPool(10);

//create a list of Futures, so you know when each tick is done
final List<Future> futures = new ArrayList<>();
for (final Map.Entry<String, CustomClass> entry : assocs.entrySet()) {
  final Future<?> future = exec.submit(new Runnable() {
    public void run() {
      String key = entry.getKey();
      CustomClass value = entry.getValue();
      value.tick(key);
    }
  });
  futures.add(future);
}

// wait for each future to complete before moving on
for (final Future<?> future : futures) {
  try {
    future.get();
  } catch (Execption e) {
    e.printStackTrace();
  }
}

强烈建议您同时以多线程和单线程方式运行代码。除非value.tick 需要非常 很长时间,否则在一个线程中执行此操作可能会更快;每当运行多线程代码时,Java 必须做很多无形的开销工作,以确保不会以不一致的方式访问内存。

【讨论】:

  • ExecutorServiceRunnableFuture 来自哪些软件包?
  • 都是核心java类:java.util.concurrent.ExecutorServicejava.lang.Runnablejava.util.concurrent.Future
  • 期货是否重要?我真的不在乎滴答声什么时候完成,或者即使它们完成了
  • 如果您真的不在乎刻度是否完成,您不必绝对必须等待future.get。但是,这会使您面临诸如“调用 tick 之前 java 已退出”或“tick 中存在错误,没有人捕获异常,因此您永远不会发现它”之类的情况。
猜你喜欢
  • 2017-05-19
  • 2015-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-22
  • 2021-02-21
  • 2020-06-14
  • 2019-02-15
相关资源
最近更新 更多