2015-12-11 17 views
5

Tôi tình cờ gặp một vấn đề bằng cách sử dụng invokeAndWait. Mã ví dụ bên dưới minh họa vấn đề. Bất cứ ai có thể xây dựng trên những gì đang xảy ra? Tại sao biểu thức lambda treo trong khi lớp bên trong vô danh và phương thức ref không.invokeAndWait với biểu thức lambda treo mãi mãi trong bộ khởi tạo tĩnh

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

Print lớp:

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

chỉ sử dụng invokeAndWait nếu bạn THỰC HIỆN biết bạn đang làm gì. –

+0

Tôi biết những gì tôi đang làm - đôi khi invokeAndWait là cần thiết và cho đến khi điều này tôi chưa bao giờ có vấn đề với nó. – Martin

+0

Vì vậy, nó chỉ tay trên lambda? Nếu bạn thay đổi thứ tự của cuộc gọi, nó vẫn chỉ treo trên lambda? –

Trả lời

4

Điều này là do biểu thức lambda (một phần) được biên dịch thành một phương thức bên trong Test, trong khi tham chiếu phương thức và phương thức của lớp bên trong vô danh thì không.

Từ Translation of Lambda Expressions:

Khi trình biên dịch gặp một biểu thức lambda, đầu tiên nó làm giảm (desugars) cơ thể lambda thành một phương pháp mà danh sách đối số và kiểu trả về trận đấu đó của biểu thức lambda, có thể với một số đối số bổ sung (cho các giá trị thu được từ phạm vi từ vựng, nếu bất kỳ.)

...

01.235.

Tham chiếu phương pháp được xử lý giống như biểu thức lambda, ngoại trừ hầu hết các tham chiếu phương pháp không cần phải được đưa vào phương thức mới ;

Bạn có thể xác minh điều này bằng cách xem mã bytecode được tạo từ việc biên dịch các lớp của bạn.

Lý do quan trọng là khi chuỗi sự kiện xếp hàng cố gắng thực hiện phương thức được tạo bởi desugaring cơ thể lambda, khối sẽ đợi chuỗi đầu tiên kết thúc intializing Test và hai chuỗi trở nên bế tắc.

Thủ tục khởi tạo được mô tả trong section 12.4 của JLS:

Một lớp học hoặc giao diện kiểu T sẽ được khởi tạo ngay trước khi sự xuất hiện đầu tiên của bất kỳ một trong các cách sau:

  • Một phương thức tĩnh được khai báo bởi T được gọi.

...

Nếu đối tượng Class cho C chỉ khởi đó là cơ bản dở dang cho C bởi một số chủ đề khác, sau đó phát hành LC và chặn hiện hành chủ đề cho đến khi thông báo rằng khởi tạo trong tiến trình đã hoàn thành , tại thời điểm đó lặp lại bước này.

Cũng trong section 5.5 của JVM:

Khi thực hiện một hướng dẫn getstatic, putstatic, hoặc invokestatic, lớp hoặc giao diện mà tuyên bố lĩnh vực giải quyết hay phương pháp được khởi tạo nếu nó không có đã được khởi tạo.

Xem this question cho ví dụ tương tự không có lambda.

+0

Cảm ơn, điều này có ý nghĩa - ít nhất là để giải thích hành vi. – Martin

1

Lưu ý câu trả lời này chỉ và khoảng Làm thế nào thực sự là chủ đề ban đầu làm việc với các phương pháp tĩnh trong Java8 là, nó không tính đến các hành vi trong thời gian chạy

  • thử nghiệm đơn giản bằng cách sử dụng invokeLater khá agr ee với ý kiến ​​ở đây, để hiểu rõ hơn

  • có vẻ như là lỗi hoặc tính năng trong JDK8 và Swing API (?? giống như lỗi hoặc tính năng về loại bỏ tất cả các phương pháp Safe Chủ đề trong JDK7)


  • martin đã viết - Tôi biết những gì tôi đang làm - đôi khi invokeAndWait là cần thiết và cho đến khi điều này tôi chưa bao giờ có vấn đề với nó. - chưa bao giờ thấy tình huống này, không bao giờ cần thiết để sử dụng invokeAndWait trong Todays Java phiên bản Java 1.6 và không bao giờ phiên bản


  • AWT tổ chức sự kiện hàng đợi cần initializations của AWT JComponents/Swing

  • API cho AWT/Swing GUI doesn 'bảo đảm trật tự t sự kiện


  1. bằng cách sử dụng tiêu chuẩn invokeLater là đầu ra không có thành phần (J), mọi thứ đều là o.k., tất cả ba luồng đều kết thúc thành công

.

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. bằng cách sử dụng tiêu chuẩn invokeLater và JFrame, mọi thứ đều ok, cả ba chủ đề kết thúc với JFrame trên màn hình, thành công (điều này là chính xác đầu ra, Đang chờ đó)

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. bằng cách sử dụng tiêu chuẩn invokeAndWait không (J) Linh kiện, không bao giờ kết thúc từ lambda expresion, nó phải bị giết từ 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. bằng cách sử dụng invokeAndWait và JFrame

không bao giờ cho thấy JFrame khởi tạo từ lambda expresion, nó phải bị giết từ 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) 

.

từ mã

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

.

+0

Cảm ơn câu trả lời của bạn. Tôi biết sự khác biệt giữa invokeLater và invokeAndWait và có cái cũ được sử dụng nhiều hơn thứ hai, và trong trường hợp của tôi, tôi cần phải sử dụng invokeAndWait và rõ ràng là một lỗi ở đâu đó. Tôi chỉ muốn biết nếu có ai khác đã quan sát nó hoặc nếu đã có một báo cáo về nó. – Martin

+0

@Martin bạn phải báo cáo là một lỗi mới, bởi điếc không có (không thể là một số, cũng không nhỏ :-) diffence, [có thể phải kiểm tra] (https://docs.oracle.com/javase /8/docs/api/java/awt/EventQueue.html#invokeAndWait-java.lang.Runnable) bằng cách sử dụng tiêu chuẩn [Initial Thread] (https://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial .html), chắc chắn nó không giúp bạn bằng cách nào đó nếu được gọi là trong thời gian chạy, thay vì invokeLater – mKorbel

1

Lưu ý câu trả lời này chỉ và khoảng Làm thế nào thực sự là chủ đề ban đầu làm việc với các phương pháp tĩnh trong Java8 là, nó không tính đến hành vi trong thời gian chạy

  • Bạn phải kiểm tra bằng sử dụng Chỉ tiêu Ban đầu chuẩn, phải có invokeLater được sử dụng nếu không lỗi

  • Chắc chắn nó không giúp bạn bằng cách nào đó nếu được gọi trong thời gian chạy, nhưng phải kiểm tra ...

  • Nó phải được khởi tạo trong Java8 bởi sử dụng invokeLater, mọi thứ có vẻ như là kết thúc cho việc sử dụng hợp lý InvokeAndWait chỉ - "Khi xây dựng tiêu chuẩn và khởi tạo của nó kết thúc từ invokeLater"

    ví dụ

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

    Và (trò đùa hài hước trong Java8) không quan trọng nếu có một số JComponents hay không khi so sánh với Java7 hoặc Java6

  • Without JComponents

    • initializer bắt đầu

      • lớp bên trong vô danh: Print.print

      • phương pháp ref: Print.print

        • Print.in gọi từ - vô danh bên trong lớp

        • lambda: Print.print
          Print.print gọi từ - phương pháp ref

        • cuối initializer
          Print.print gọi từ - lambda

  • Bằng việc sử dụng JFrame như JComponent

    enter image description here

    • initializer bắt đầu

      • vô danh bên trong lớp: Print.print

      • phương pháp ref: Print.print

      • lambda: Print.print

      • initializer cuối

      • Print.print gọi từ - vô danh bên trong lớp
        ** ** Print.print gọi từ - phương pháp ref
      • Print.print gọi từ - lambda
      • Print.print gọi từ - lambda
Các vấn đề liên quan