6

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();
    }
}

The Print class:

public class Print {
    public static void print() {
        System.out.println("Print.print");
    }
}
Martin
  • 63
  • 3
  • 2
    only use invokeAndWait if you REALLY know what you are doing. – Oliver Watkins Dec 11 '15 at 12:34
  • I know what I'm doing - sometimes invokeAndWait is necessary and until this I've never had issues with it. – Martin Dec 11 '15 at 13:01
  • So it only hands on the lambda? If you change order of calls it still just hangs on the lambda? – Oliver Watkins Dec 11 '15 at 13:04
  • Yes, you can comment the two others out, the lambda hangs. I could start looking at bytecode, but I thought I'd ask here first. – Martin Dec 11 '15 at 13:06
  • - and that whats triggers me, if they all behaved the same I'd say its my own "fault" - but this makes me suspicious, they SHOULD behave the same IMO. – Martin Dec 11 '15 at 13:09
  • hmm. not sure. I think mKorbel has some good info tho. Good luck ! – Oliver Watkins Dec 11 '15 at 13:15
  • I get exactly this problem. I was trying to use `invokeAndWait` by inputting a lambda expression that sets parameters on a newly created `JFrame`. Has this been fixed in the latest Java SE 1.8 build? – ThePyroEagle Dec 22 '15 at 14:09

3 Answers3

5

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.

From Translation of Lambda Expressions:

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.)

...

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.

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.

The initialization procedure is described in section 12.4 of the JLS:

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

  • A static method declared by T is invoked.

...

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.

Also in section 5.5 of the JVMS:

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.

See this question for a similar example without lambdas.

Community
  • 1
  • 1
Alex - GlassEditor.com
  • 14,957
  • 5
  • 49
  • 49
1

Note this answer is just and about How really the Initial Thread works with static methods in Java8, it doesn't account for the behavior at runtime

  • simple testing by using invokeLater quite agree with comments here, for better understanding

  • seems like as bug or feature in JDK8 and Swing APIs (??the same as bug or feature about removing all Thread Safe methods in JDK7 )


  • martin wrote - I know what I'm doing - sometimes invokeAndWait is necessary and until this I've never had issues with it. - never seen this situations, never needed to use invokeAndWait in todays Java versions Java 1.6 and never versions


  • AWT Event queue required initializations of AWT/Swing JComponents

  • APIs for AWT/Swing GUI doesn't guarantee ordering of events


  1. by using standard invokeLateris output without (J)Components, everything is o.k., all three threads ends with success

.

initializer start

 - anonymous inner-class: Print.print

 - method ref: Print.print
 * Print.print called from - anonymous inner-class

 - lambda: Print.print
 ** ** Print.print called from - method ref

 - initializer end
 * Print.print called from - lambda
BUILD SUCCESSFUL (total time: 1 second)

  1. by using standard invokeLater and JFrame , everything is o.k., all three threads ends with JFrame on the screen, success (this is correct output, awating that)

enter image description here .

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

  1. by using standard invokeAndWait without (J)Components, never ends from lambda expresion, it must be killed from IDE

.

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

  1. by using invokeAndWait and JFrame

never shows jframe initialized from lambda expresion, it must be killed from IDE

enter image description here .

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)

.

from code

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();
    }
}

and

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);
    }
}

.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Thanks for your answer. I know the difference between invokeLater and invokeAndWait and yes the the former is used much more than the latter, and in my case I need to use invokeAndWait and there clearly is a bug somewhere. I just wanted to know if anyone else had observed it or if there already was a bugreport on it. – Martin Dec 11 '15 at 13:18
  • @Martin you have to report as a new bug, by deafult there isn't (could'n be some, nor small:-) diffence, [maybe have to testing](https://docs.oracle.com/javase/8/docs/api/java/awt/EventQueue.html#invokeAndWait-java.lang.Runnable) by using standard [Initial Thread](https://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html), sure it doesn't help you somehow if is called at runtime, instead of invokeLater – mKorbel Dec 11 '15 at 13:26
1

Note this answer is just and about How really the Initial Thread works with static methods in Java8, it doesn't account for the behavior at runtime

  • You have to test by using standard Initial Thread, there must be invokeLater used otherwise its fails

  • Sure it doesn't help you somehow if is called at runtime, but have to test about ...

  • It must be initialized in Java8 by using invokeLater, everything seems like as ending for reasonable usage of InvokeAndWait only - "When standard constructor ends and its initialized from invokeLater"

    e.g.

    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();
        }
    }
    

    And (funny joke in Java8) doesn't matter if is there some JComponents or not in comparing with Java7 or Java6

  • Without JComponents

    • initializer start

      • anonymous inner-class: Print.print

      • method ref: Print.print

        • Print.print called from - anonymous inner-class

        • lambda: Print.print
          Print.print called from - method ref

        • initializer end
          Print.print called from - lambda

  • By using JFrame as JComponent

    enter image description here

    • 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
      • Print.print called from - lambda
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
mKorbel
  • 109,525
  • 20
  • 134
  • 319