带有lambda表达式的invokeAndWait在静态初始化程序中永远挂起 [英] invokeAndWait with lambda expression hangs forever in static initializer

查看:96
本文介绍了带有lambda表达式的invokeAndWait在静态初始化程序中永远挂起的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用invokeAndWait偶然发现了一个问题。下面的示例代码说明了该问题。任何人都可以详细说明发生了什么事吗?为什么lambda表达式挂起而匿名内部类和方法ref没有。

I stumbled across an issue using invokeAndWait. The example code below illustrates the issue. Can anyone elaborate on whats happening? Why the lambda expression hangs while the anonymous inner class and method ref doesn't.

public class Test {
    // A normal (non-static) initializer does not have the problem
    static {
        try {
            System.out.println("initializer start");

            // --- Works
            System.out.println("\nanonymous inner-class: Print.print");
            EventQueue.invokeAndWait(new Runnable() {
                @Override
                public void run() {
                    Print.print();
                }
            });

            // --- Works
            System.out.println("\nmethod ref: Print.print");
            EventQueue.invokeAndWait(Print::print);

            // --- Hangs forever
            System.out.println("\nlambda: Print.print");
            EventQueue.invokeAndWait(() -> Print.print());

            System.out.println("\ninitializer end");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Test();
    }
}

打印类:

public class Print {
    public static void print() {
        System.out.println("Print.print");
    }
}


推荐答案

这是因为lambda表达式(部分)编译成 Test 中的方法,而方法引用和匿名内部类的方法不是。

This is because the lambda expression is (in part) compiled into a method inside Test, while the method reference and the method of the anonymous inner class are not.

来自 Lambda表达式的翻译


当编译器遇到lambda表达式时,它首先将
(desugars)的lambda体降低为a参数列表和
返回类型的方法匹配lambda表达式的方法,可能还有一些
附加参数(对于从词法范围捕获的值,如果
any。)

When the compiler encounters a lambda expression, it first lowers (desugars) the lambda body into a method whose argument list and return type match that of the lambda expression, possibly with some additional arguments (for values captured from the lexical scope, if any.)

...


方法引用的处理方式与lambda表达式,
除了大多数方法引用不需要被去掉
新方法;

Method references are treated the same way as lambda expressions, except that most method references do not need to be desugared into a new method;

您可以通过查看编译类产生的字节码来验证这一点。

You can verify this by looking at the bytecode produced from compiling your classes.

这个问题的原因是,当事件队列线程尝试执行通过取消lambda体创建的方法时,它会阻塞等待第一个线程完成初始化测试,并且两个线程变得死锁。

The reason this matters is that when the event queue thread attempts to execute the method created by desugaring the lambda body it blocks waiting for the first thread to finish intializing Test, and the two threads become deadlocked.

初始化过程在第12.4节


类或接口类型T将在
首次出现以下任何一项之前立即初始化:

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:


  • 调用T声明的静态方法。

...


如果C的Class对象表明初始化在progr中通过其他线程获取C的
,然后释放LC并阻止当前的
线程,直到通知正在进行的初始化已完成
,此时重复此步骤。

If the Class object for C indicates that initialization is in progress for C by some other thread, then release LC and block the current thread until informed that the in-progress initialization has completed, at which time repeat this step.

同样在第5.5节


执行getstatic ,putstatic或invokestatic指令,
声明已解析字段或方法的类或接口如果尚未初始化则为
初始化。

Upon execution of a getstatic, putstatic, or invokestatic instruction, the class or interface that declared the resolved field or method is initialized if it has not been initialized already.

请参阅这个问题没有lambdas的类似例子。

See this question for a similar example without lambdas.

这篇关于带有lambda表达式的invokeAndWait在静态初始化程序中永远挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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