为什么在静态初始化程序中使用lambda的并行流导致死锁? [英] Why does parallel stream with lambda in static initializer cause a deadlock?

查看:255
本文介绍了为什么在静态初始化程序中使用lambda的并行流导致死锁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个奇怪的情况,即在静态初始化程序中使用带有lambda的并行流看似永远没有CPU利用率。这是代码:

I came across a strange situation where using a parallel stream with a lambda in a static initializer takes seemingly forever with no CPU utilization. Here's the code:

class Deadlock {
    static {
        IntStream.range(0, 10000).parallel().map(i -> i).count();
        System.out.println("done");
    }
    public static void main(final String[] args) {}
}

这似乎是此行为的最小再现测试用例。如果我:

This appears to be a minimum reproducing test case for this behavior. If I:


  • 将块放在main方法而不是静态初始化程序中,

  • 删除并行化,或

  • 删除lambda,

代码立即完成。谁能解释这种行为?这是一个bug还是这个?

the code instantly completes. Can anyone explain this behavior? Is it a bug or is this intended?

我使用的是OpenJDK版本1.8.0_66-internal。

I am using OpenJDK version 1.8.0_66-internal.

推荐答案

我发现了一个非常类似案例的错误报告( JDK-8143380 )由Stuart Marks关闭为Not an Issue:

I found a bug report of a very similar case (JDK-8143380) which was closed as "Not an Issue" by Stuart Marks:


这是一个类初始化死锁。测试程序的主线程执行类静态初始化程序,它设置类的初始化进程中标志;静态初始化程序完成之前,此标志保持设置状态。静态初始化程序执行并行流,这会导致在其他线程中计算lambda表达式。这些线程阻止等待类完成初始化。但是,主线程被阻塞等待并行任务完成,导致死锁。

This is a class initialization deadlock. The test program's main thread executes the class static initializer, which sets the initialization in-progress flag for the class; this flag remains set until the static initializer completes. The static initializer executes a parallel stream, which causes lambda expressions to be evaluated in other threads. Those threads block waiting for the class to complete initialization. However, the main thread is blocked waiting for the parallel tasks to complete, resulting in deadlock.

应该更改测试程序以将并行流逻辑移到类外静态初始化器。关闭不是问题。

The test program should be changed to move the parallel stream logic outside of the class static initializer. Closing as Not an Issue.






我能够找到另一个错误报告( JDK8136753 ),也被Stuart Marks称为非问题:


I was able to find another bug report of that (JDK-8136753), also closed as "Not an Issue" by Stuart Marks:


这是一个死锁,因为Fruit enum的静态初始化程序与类初始化交互不良。

This is a deadlock that is occurring because the Fruit enum's static initializer is interacting badly with class initialization.

请参阅Java语言规范,第12.4.2节有关类初始化的详细信息。

See the Java Language Specification, section 12.4.2 for details on class initialization.

http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4.2

简而言之,发生的事情如下。

Briefly, what's happening is as follows.


  1. 主线程参考Fruit类并启动初始化过程。这将设置初始化进行中标志并在主线程上运行静态初始化程序。

  2. 静态初始化程序在另一个线程中运行一些代码并等待它完成。此示例使用并行流,但这与流本身无关。通过任何方式在另一个线程中执行代码,并等待该代码完成,将产生相同的效果。

  3. 另一个线程中的代码引用Fruit类,它检查初始化-progress标志。这会导致另一个线程阻塞,直到清除该标志。 (参见JLS 12.4.2的第2步。)

  4. 主线程被阻塞,等待另一个线程终止,因此静态初始化器永远不会完成。由于初始化进行中标志直到静态初始化程序完成后才被清除,因此线程处于死锁状态。

  1. The main thread references the Fruit class and starts the initialization process. This sets the initialization in-progress flag and runs the static initializer on the main thread.
  2. The static initializer runs some code in another thread and waits for it to finish. This example uses parallel streams, but this has nothing to do with streams per se. Executing code in another thread by any means, and waiting for that code to finish, will have the same effect.
  3. The code in the other thread references the Fruit class, which checks the initialization in-progress flag. This causes the other thread to block until the flag is cleared. (See step 2 of JLS 12.4.2.)
  4. The main thread is blocked waiting for the other thread to terminate, so the static initializer never completes. Since the initialization in-progress flag isn't cleared until after the static initializer completes, the threads are deadlocked.

要避免此问题,确保类的静态初始化快速完成,而不会导致其他线程执行需要此类完成初始化的代码。

To avoid this problem, make sure that a class's static initialization completes quickly, without causing other threads to execute code that requires this class to have completed initialization.

关闭不是问题。






请注意 FindBugs有一个未解决的问题,即为此情况添加警告

这篇关于为什么在静态初始化程序中使用lambda的并行流导致死锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆