Java 带有lambda表达式的invokeAndWait在静态初始值设定项中永久挂起

Java 带有lambda表达式的invokeAndWait在静态初始值设定项中永久挂起,java,swing,lambda,Java,Swing,Lambda,我在使用invokeAndWait时偶然发现了一个问题。下面的示例代码说明了这个问题。有人能详细说明发生了什么事吗?为什么lambda表达式挂起而匿名内部类和方法ref不挂起 public class Test { // A normal (non-static) initializer does not have the problem static { try { System.out.println("initializer star

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

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");
    }
}
注意,这个答案只是关于初始线程如何在Java8中与静态方法一起工作,它 不考虑运行时的行为

  • 为了更好地理解,使用invokeLater进行的简单测试与这里的注释非常一致

  • 类似于JDK8和Swing API中的bug或特性(??与删除JDK7中所有线程安全方法的bug或特性相同)



  • 马丁写道——我知道我在做什么——有时调用EANDWAIT是必要的,在此之前,我从未对此有过异议从未见过这种情况,从未在今天的Java版本Java 1.6和never版本中需要使用invokeAndWait


  • AWT事件队列需要对AWT/Swing JComponents进行初始化

  • AWT/Swing GUI的API不能保证事件的顺序


  • 通过使用标准的
    invokeLater
    输出时没有(J)个组件,一切正常,所有三个线程都成功结束

  • 通过使用标准的
    invokeLater
    和JFrame,一切正常,所有三个线程都以屏幕上的JFrame结束,success(这是正确的输出,唤醒它)

  • 通过使用不带(J)组件的标准
    invokeAndWait
    ,它永远不会从lambda表达式结束,必须从IDE中删除它

  • 通过使用
    invokeAndWait
    和JFrame
  • 从不显示从lambda expresion初始化的jframe,它必须从IDE终止

    源代码

    public class Test {
    
        // A normal (non-static) initializer does not have the problem 
        static {
            try {
                System.out.println("initializer start");
                // --- Works
                System.out.println("\n - anonymous inner-class: Print.print");
                EventQueue.invokeLater/*EventQueue.invokeAndWait*/(new Runnable() {
                            @Override
                            public void run() {
                                Print.print("anonymous inner-class");
                            }
                        });
                // --- Works
                System.out.println("\n - method ref: Print.print");
                EventQueue.invokeLater/*EventQueue.invokeAndWait*/(Print::print);
                // --- Hangs forever
                System.out.println("\n - lambda: Print.print");
                EventQueue.invokeLater/*EventQueue.invokeAndWait*/(() -> Print.print("lambda"));
                System.out.println("\n - initializer end");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            new Test();
        }
    }
    

    注意,这个答案只是关于初始线程如何在Java8中与静态方法一起工作,它 不考虑运行时的行为

    • 您必须使用标准的初始线程进行测试,必须使用
      invokeLater
      ,否则它将失败

    • 当然,如果在运行时调用,它对您没有帮助,但是必须测试关于

    • 它必须通过使用
      invokeLater
      在Java8中初始化,对于合理使用
      InvokeAndWait
      而言,一切似乎都只是结束——“当标准构造函数结束并从
      invokeLater
      初始化时。”

      e、 g

      而且(Java8中的有趣笑话)与Java7或Java6相比,是否有一些JComponents并不重要

    • 没有组件

      • 初始化器启动

        • 匿名内部类:Print.Print

        • 方法参考:Print.Print

          • Print.Print从调用-匿名内部类

          • lambda:
            Print.Print

            Print.Print
            调用自-method ref

          • 初始值设定项结束
            Print.Print
            从-lambda调用

    • 使用JFrame作为JComponent

      • 初始化器启动

        • 匿名内部类:
          Print.Print

        • 方法参考:
          Print.Print

        • lambda:
          Print.Print

        • 初始化器端

        • Print.Print从调用-匿名内部类
          ****Print.Print从调用-方法参考
        • Print.Print从-lambda调用
        • Print.Print从-lambda调用

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

    发件人:

    当编译器遇到lambda表达式时,它首先降低 (desugars)将lambda体转换为参数列表和 返回类型与lambda表达式的返回类型匹配,可能与 附加参数(用于从词法范围捕获的值,如果需要) 任何人。)

    方法引用的处理方式与lambda表达式相同, 除了大多数方法引用不需要被分解成 新方法

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

    这一点很重要的原因是,当事件队列线程尝试执行通过对lambda主体进行去糖化而创建的方法时,它会阻止等待第一个线程完成初始化
    Test
    ,这两个线程会陷入死锁

    初始化过程在JLS的第页中描述:

    类或接口类型T将在 首次出现以下任何一种情况:

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

    如果C的类对象指示正在进行初始化 对于C,通过其他线程,然后释放LC并阻塞电流 线程,直到通知正在进行的初始化已完成 已完成,此时重复此步骤

    另外,在JVM中:

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


    请参阅不带lambdas的类似示例。

    仅当您真正了解自己在做什么时才使用invokeAndWait。我知道自己在做什么-有时invokeAndWait是必要的,在此之前,我从未遇到过问题。所以它只涉及lambda?如果你改变了调用顺序,它仍然挂在lambda上?是的,你可以注释掉另外两个,lambda挂起。我可以开始看字节码,但我想我应该先问一下。-是什么触发了我,如果他们的行为都一样,我会说这是我自己的“错”-但这让我怀疑,他们的行为在我看来应该是一样的
    initializer start
    
     - anonymous inner-class: Print.print
    
     - method ref: Print.print
    
     - lambda: Print.print
    
     - initializer end
     * Print.print called from - anonymous inner-class
     ** ** Print.print called from - method ref
     * Print.print called from - lambda
    
    initializer start
    
     - anonymous inner-class: Print.print
     * Print.print called from - anonymous inner-class
    
     - method ref: Print.print
     ** ** Print.print called from - method ref
    
     - lambda: Print.print
    
    run:
    initializer start
    
     - anonymous inner-class: Print.print
     * Print.print called from - anonymous inner-class
    
     - method ref: Print.print
     ** ** Print.print called from - method ref
    
     - lambda: Print.print
    BUILD STOPPED (total time: 3 minutes 40 seconds)
    
    public class Test {
    
        // A normal (non-static) initializer does not have the problem 
        static {
            try {
                System.out.println("initializer start");
                // --- Works
                System.out.println("\n - anonymous inner-class: Print.print");
                EventQueue.invokeLater/*EventQueue.invokeAndWait*/(new Runnable() {
                            @Override
                            public void run() {
                                Print.print("anonymous inner-class");
                            }
                        });
                // --- Works
                System.out.println("\n - method ref: Print.print");
                EventQueue.invokeLater/*EventQueue.invokeAndWait*/(Print::print);
                // --- Hangs forever
                System.out.println("\n - lambda: Print.print");
                EventQueue.invokeLater/*EventQueue.invokeAndWait*/(() -> Print.print("lambda"));
                System.out.println("\n - initializer end");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            new Test();
        }
    }
    
    import javax.swing.JFrame;
    
    public class Print {
    
        public static final void print() {
            /*
            JFrame frame = new JFrame();
            frame.setTitle("called from - method ref");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(400, 300);
            frame.setLocationByPlatform(true);
            frame.setVisible(true);*/
            System.out.println(" ** ** Print.print called from - method ref");
        }
    
        public static final void print(String str) {
            /*
            JFrame frame = new JFrame();
            frame.setTitle("called from - " + str);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(400, 300);
            frame.setLocationByPlatform(true);
            frame.setVisible(true);*/
            System.out.println(" * Print.print called from - " + str);
        }
    }
    
    import java.awt.EventQueue;
    import java.lang.reflect.InvocationTargetException;
    
    public class Test {
    
        // A normal (non-static) initializer does not have the problem 
        static {
            try {
                System.out.println("initializer start");
                // --- Works
                System.out.println("\n - anonymous inner-class: Print.print");
                EventQueue.invokeLater/*invokeAndWait*/(new Runnable() {
                            @Override
                            public void run() {
                                Print.print("anonymous inner-class");
                            }
                        });
                // --- Works
                System.out.println("\n - method ref: Print.print");
                EventQueue.invokeLater/*invokeAndWait*/(Print::print);
                // --- Hangs forever
                System.out.println("\n - lambda: Print.print");
                EventQueue.invokeLater/*invokeAndWait*/(() -> Print.print("lambda"));
                System.out.println("\n - initializer end");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) throws InterruptedException, InvocationTargetException {
            EventQueue.invokeAndWait(() -> Print.print("lambda"));
            new Test();
        }
    }