Java 为什么我不';不要使用;“lambda方法参考”;静态块中的代码样式是否会导致死锁?

Java 为什么我不';不要使用;“lambda方法参考”;静态块中的代码样式是否会导致死锁?,java,lambda,java-8,jvm,jvm-crash,Java,Lambda,Java 8,Jvm,Jvm Crash,对不起,我的英语不好。我不能清楚地描述这个问题。 :( 当我在静态块中不使用“lambda方法引用”代码样式时,如: static{ map.keySet().parallelStream().forEach(e -> { System.out.println(e); }); } 然后程序永远运行,永不停止 但是当我把代码改成 static{ map.keySet().parallelStream().forEach(System.out::pri

对不起,我的英语不好。我不能清楚地描述这个问题。 :(

当我在静态块中不使用“lambda方法引用”代码样式时,如:

static{
map.keySet().parallelStream().forEach(e -> {
            System.out.println(e);
        });
}
然后程序永远运行,永不停止

但是当我把代码改成

static{
map.keySet().parallelStream().forEach(System.out::println);
}
然后错误消失了。程序可以立即完成

请直接看一下代码,我已经尽力简化了代码

public class BugSimulate {

    static {
        init();
    }

    private static void init() {
        Map<Integer, String> map = new HashMap<>();

        int i = 0;
        map.put(++i, "1");
        map.put(++i, "1");
        map.put(++i, "1");
        map.put(++i, "1");
        map.put(++i, "1");
        map.put(++i, "1");
        map.put(++i, "1");
        map.put(++i, "1");
        map.put(++i, "1");

        // running forever
        map.keySet().parallelStream().forEach(e -> {
            System.out.println(e);
        });

        // finish immediately
        //        map.keySet().parallelStream().forEach(System.out::println);
    }

    @Test
    public void test() {
        new BugSimulate();
    }
}

节目马上就结束了

或者我将parallelStream()更改为normal stream(),bug消失了

或者我移除了静态块,bug也消失了

我的jdk版本是1.8.0202


操作系统版本是MacOS 10.14.5

有趣的问题:这个问题与

但是对于这个问题,它是由使用
e->{System.out.println(e);}
时引起的,在编译之后,它将为此生成一个合成方法:匿名静态私有方法,如下所示:

  // access flags 0x100A
  private static synthetic lambda$init$0(Ljava/lang/Integer;)V
   L0
    LINENUMBER 27 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 0
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L1
    LINENUMBER 28 L1
    RETURN
   L2
    LOCALVARIABLE e Ljava/lang/Integer; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1
这个合成方法:私有静态方法
e->{System.out.println(e);}
compile生成

它也类似于:

static {
    init();
}

private static void init() {
    Map<Integer, String> map = new HashMap<>();

    int i = 0;
    map.put(++i, "1");
    ***
    Thread foo = new Thread() {
        @Override
        public void run() {
            print(map.keySet().iterator().next()); //try access the private method, this cause a competition with **Class Initializer** and **static block**
        }
    };

    foo.start();
    try {
        foo.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
//equal to e -> {System.out.println(e);}
private static void print(int t) {
    System.out.println(t);
}
静态{
init();
}
私有静态void init(){
Map Map=newhashmap();
int i=0;
map.put(++i,“1”);
***
线程foo=新线程(){
@凌驾
公开募捐{
print(map.keySet().iterator().next());//尝试访问私有方法,这会导致与**类初始值设定项**和**静态块的竞争**
}
};
foo.start();
试一试{
foo.join();
}捕捉(中断异常e){
e、 printStackTrace();
}
}
//等于e->{System.out.println(e);}
专用静态无效打印(int t){
系统输出打印ln(t);
}
正如本文的解释:当init classbugmasulate时,它将首先调用
静态块
,但由于它使用并行流,并试图调用生成的私有匿名方法,这导致了与类初始值设定项的竞争,因此最终导致死锁


你说的“卡住”是什么意思?你看到了什么意外行为?@Eran对此非常抱歉。我刚刚重新编辑了我的描述。请再读一遍。这很有趣。我已经运行了“错误”代码,它工作时没有死锁。以及为什么这个变体正确工作
System.out::println
?@v.ladynev当使用
System.out::println
时将直接调用这个静态方法:
GETSTATIC java/lang/System.out:Ljava/io/PrintStream;
非常感谢,现在我明白了。lambda中的匿名方法将在ru生成但正常的匿名方法是在编译时生成的。这是非常重要的一点,我不知道,然后导致了我的错误confusion@ACBingo但是值得一提的是,您不应该依赖微妙的实现细节,例如,while
Stream.of(“a”、“b”、“c”、“d”).parallel().forEach(System.out::println)
不会死锁,因为它创建了一个直接委托,
Stream.of(“a”、“b”、“c”、“d”).parallel().forEach(System.out::printf);
可能会死锁,因为它会调用类的合成方法(如lambda表达式)来准备varargs调用的数组。链接Q&a的好处是,通常应该避免在类初始值设定项中进行多线程操作。@ACBingo合成方法是在编译时生成的。实现t的类函数接口将在运行时生成。始终会有运行时生成的类,但对于某些方法引用,它将直接调用目标方法。相反,对于lambda表达式和某些非平凡的方法引用(varargs、交集类型、特殊访问修饰符),它将调用类中编译器生成的方法,这可能导致这些死锁。
  // access flags 0x100A
  private static synthetic lambda$init$0(Ljava/lang/Integer;)V
   L0
    LINENUMBER 27 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 0
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
   L1
    LINENUMBER 28 L1
    RETURN
   L2
    LOCALVARIABLE e Ljava/lang/Integer; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1
static {
    init();
}

private static void init() {
    Map<Integer, String> map = new HashMap<>();

    int i = 0;
    map.put(++i, "1");
    ***
    Thread foo = new Thread() {
        @Override
        public void run() {
            print(map.keySet().iterator().next()); //try access the private method, this cause a competition with **Class Initializer** and **static block**
        }
    };

    foo.start();
    try {
        foo.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
//equal to e -> {System.out.println(e);}
private static void print(int t) {
    System.out.println(t);
}