它是可以平行的吗?

发布于 2025-01-26 14:32:06 字数 533 浏览 2 评论 0 原文

从5点起使用Java,今天我在a :: 有人使用列表#foreach 在Java 8中使用 AtomicInteger

现在,我想知道这是否可能是必要的 - lo and Chood,请咨询

除非实施类另有指定,否则按迭代顺序执行操作(如果指定了迭代顺序)。

(重点是我的)

...因此,鉴于不是实现 iToble (从Java 18)的点 - 我应该真的应该真的吗必须假定 Itable 的未知实施者可能会在并行吗?

(如果期望将根据Java版本进行更改,我希望答案可以从8开始处理每个不同版本。)

Using Java since 5, today I encountered an interesting piece of code in a GitHub PR:
Someone used List#forEach with an AtomicInteger in Java 8.

Now I wondered whether this could possibly be necessary - and lo and behold, consulting the Java 8 Documentation (where Iterable#forEach was introduced, as well as the lookalike stream().parallel().forEach, the latter of which obviously might be parallel) I read:

Unless otherwise specified by the implementing class, actions are performed in the order of iteration (if an iteration order is specified).

(emphasis mine)

... So, given that Stream makes a point of not implementing Iterable (as of Java 18) - should I really have to presume that an unknown implementor of Iterable might do forEach in parallel?

(If the expectation would have change based on Java version, I'd like an answer to deal with each differing version starting at 8.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

陪你搞怪i 2025-02-02 14:32:06

请注意,“除非实现类另有指定”的短语已从规范的最新版本

iTable 的每个元素执行给定的操作,直到处理所有元素或该动作引发异常为止。如果指定了该顺序,则操作是按迭代顺序执行的。动作抛出的例外传达给呼叫者。

由于 list 具有定义的遭遇顺序,并在定义的顺序中执行操作,因此我们可以清楚地说,在问题中提到的具体情况下,同时执行被明确排除。

The broader question whether Iterable's forEach precludes concurrent execution of the specified action in general, when there's no defined encounter order, can only be answered by resorting to the 至少令人惊讶的原则,结论是,如果在某些情况下同时执行,则规格应明确提及。据我所知,Java的整个API规范遵守此原则,因此没有理由假设它不适用于此特定方法。

最值得注意的是,每个人都知道 stream API允许并行处理,因为它已得到了重要记录。同样, arrays.parallelsort(…) 启用它。

即使是实际的并发收集也适用。例如,当您调用键()。 //docs.oracle.com/en/java/java/javase/17/docs/api/java.base/java/java/java/util/concurrent/concurrent/package-summary.html#weakly' >迭代策略指定,但同时在呼叫者的线程上依次运行,就像每个方法在另外指定时所做的那样。您需要一个专用 foreach 方法用于并行处理。


因此,使用 AtomicInteger 是没有道理的。这是一种普遍的糟糕编程样式,假装功能编程,同时仍然坚持循环思考,使用 foreach 并修改功能之外的变量。由于本地变量是不可能的,因此 atomicInteger 仅用作 int heap变量的容器。使用单个元素 int [] 数组也可以做到,但仍然不建议使用。而是

  1. 保持平原环或

  2. 使用实际功能方法,例如

      int result = words.stream()
        .maptoint(word  - > {{
            //计数与本地匹配并返回int
        }))
        。和();
     

      long结果= words.stream()
        .flatmap(word-> {
            //返回一系列比赛
        }))
        。数数();
     

Note that the phrase “Unless otherwise specified by the implementing class” has been removed from recent versions of the specification:

Performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception. Actions are performed in the order of iteration, if that order is specified. Exceptions thrown by the action are relayed to the caller.

Since a List has a defined encounter order and performing an action in a defined order precludes concurrent execution, we can clearly say that concurrent execution is precluded explicitly in the specific case mentioned in your question.

The broader question whether Iterable’s forEach precludes concurrent execution of the specified action in general, when there’s no defined encounter order, can only be answered by resorting to the Principle of least astonishment, concluding that the specification should mention explicitly if concurrent execution was allowed under certain circumstances. As far as I can see, Java’s entire API specification adheres to this principle, so there’s no reason to assume that it doesn’t apply to this specific method.

Most notably, everyone is aware that the Stream API allows parallel processing, because it has been prominently documented. Likewise, Arrays.sort(…) does not permit a surprising parallel evaluation of the Comparator, without mentioning it explicitely, but rather, an explicit use of Arrays.parallelSort(…) is required to enable it.

The same applies even for the actual concurrent collections. E.g. when you call keySet().forEach(…) on a ConcurrentHashMap, it will not fail when there are concurrent updates, as the general weakly consistent iteration policy specifies, but also run sequentially on the caller’s thread, as every method does when not specified otherwise. You’d need a dedicated forEach method for parallel processing.


So the use of AtomicInteger is not justified. It’s a widespread bad programming style, pretending functional programming while still being stuck with thinking in loops, using forEach and modifying a variable outside the function. Since this is not possible with local variables, AtomicInteger is only used as container for an int heap variable here. Using a single element int[] array would do as well, but still isn’t recommended. Instead

  1. stay with a plain loop, or

  2. use a real functional approach, e.g.

    int result = words.stream()
        .mapToInt(word -> {
            // count matches locally and return the int
        })
        .sum();
    

    or

    long result = words.stream()
        .flatMap(word -> {
            // return a stream of matches
        })
        .count();
    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文