Để giúp chúng tôi trong việc đảm bảo rằng tất cả các sự kiện xử lý mã của chúng tôi được thực hiện chỉ từ bên trong event-dispatching, Swing cung cấp một lớp học rất hữu ích đó, trong số những thứ khác, cho phép chúng tôi để thêm đối tượng Runnable vào hàng đợi sự kiện hệ thống. Lớp này được gọi là SwingUtilities và nó có chứa hai phương pháp mà chúng ta quan tâm ở đây: invokeLater () và invokeAndWait (). Phương pháp đầu tiên thêm một Runnable vào hàng đợi sự kiện hệ thống và trả về ngay lập tức. Phương pháp thứ hai cho biết thêm một Runnable và đợi cho nó để được gửi đi, sau đó trở về sau khi nó kết thúc. Cú pháp cơ bản của mỗi sau: Runnable trivialRunnable = new Runnable () { public void run () { DoWork (); // Thực hiện một số công việc } }; SwingUtilities.invokeLater (trivialRunnable); try { Runnable trivialRunnable2 = new Runnable () { public void run () { DoWork (); // Thực hiện một số công việc } }; SwingUtilities.invokeAndWait (trivialRunnable2); } catch (InterruptedException ie) { System.out.println ("... chờ đợi đề gián đoạn!"); } catch (InvocationTargetException ite) { System.out. println ( "... ngoại lệ còn tự do trong vòng chạy Runnable của ()"); } Bởi vì những Runnables được đặt vào hàng đợi sự kiện hệ thống để thực hiện trong các event-dispatching, chúng ta nên được chỉ là cẩn thận rằng họ thực hiện một cách nhanh chóng, như bất kỳ khác kiện xử lý mã. Trong hai ví dụ trên, nếu DoWork () phương pháp đã làm một cái gì đó mà phải mất một thời gian dài (như tải một tập tin lớn) chúng ta sẽ thấy rằng các ứng dụng sẽ đóng băng cho đến khi kết thúc tải. Trong trường hợp thời gian thâm canh như thế này, chúng ta nên sử dụng chủ đề riêng biệt của riêng của chúng tôi để duy trì đáp ứng. Các mã sau đây cho thấy một cách điển hình để xây dựng chủ đề của riêng của chúng tôi để làm một số công việc tốn thời gian. Để cập nhật một cách an toàn trạng thái của bất kỳ thành phần từ bên trong chủ đề này, chúng ta phải sử dụng invokeLater () hoặc invokeAndWait (): chủ workHard = new Thread () { public void run () { doToughWork (); // Thực hiện một số thực sự tốn thời gian làm việc SwingUtilities.invokeLater (new Runnable () { public void run () { updateComponents (); // cập nhật trạng thái của thành phần (s) } }); } }; workHard.start () ; Lưu ý: invokeLater () nên thay vì invokeAndWait () bất cứ khi nào có thể. Nếu chúng ta phải sử dụng invokeAndWait (), chúng ta cần phải bảo đảm rằng không có ổ khóa (khối iesynchronized) được tổ chức bởi các thread kêu gọi rằng chủ đề khác có thể cần trong quá trình hoạt động. Điều này giải quyết các vấn đề của sự đáp ứng, và nó có công văn component- code liên quan đến event-dispatching, nhưng nó vẫn không thể được coi là hoàn toàn sử dụng. Thông thường người dùng sẽ có thể làm gián đoạn một thủ tục tốn thời gian. Nếu chúng ta đang chờ đợi để thiết lập kết nối mạng, chúng tôi chắc chắn không muốn tiếp tục chờ đợi vô thời hạn nếu các điểm đến không còn tồn tại. Trong hầu hết các trường hợp, người sử dụng nên có các tùy chọn để làm gián đoạn chủ đề của chúng tôi. Các mã giả sau đây cho thấy một cách điển hình để thực hiện điều này, nơi stopButton gây ra các chủ đề để bị gián đoạn, cập nhật trạng thái thành phần phù hợp: chủ workHarder = new Thread () { public void run () { doTougherWork (); SwingUtilities.invokeLater (new Runnable () { public void run () { updateMyComponents (); // cập nhật trạng thái của thành phần (s) } }); } }; workHarder.start (); public void doTougherWork () { try { // [một số loại loop] // ... nếu, tại bất kỳ điểm nào, điều này liên quan đến việc thay đổi // trạng thái thành phần chúng ta sẽ phải sử dụng invokeLater // ở đây vì đây là một chủ đề riêng biệt. // // Chúng ta phải làm ít nhất một trong các cách sau : // 1. Định kỳ kiểm tra Thread.interrupted () // 2. Định kỳ ngủ hoặc chờ if (Thread.interrupted ()) { ném InterruptedException mới (); } Thread.wait (1000); } catch (InterruptedException e) { // cho ai đó biết chúng ta đã bị gián đoạn // ... nếu điều này liên quan đến việc thay đổi trạng thái thành phần // chúng ta sẽ phải sử dụng invokeLater đây. } } JButton stopButton = new JButton ("Stop"); ActionListener stopListener = new ActionListener () { public void (sự kiện ActionEvent) actionPerformed { // ngắt lời các chủ đề và cho phép người dùng biết // Chủ đề đã bị gián đoạn bởi việc vô hiệu hóa các nút // dừng. // ... điều này sẽ xảy ra trên các văn kiện thường xuyên stopButton ngắt thread workHarder khi ép. Có hai cách mà doTougherWork () sẽ biết liệu workHarder, thread nó được thực hiện trong, đã bị gián đoạn. Nếu nó đang ngủ hay chờ đợi, một InterruptedException sẽ được ném ra mà chúng ta có thể bắt và chế biến cho phù hợp. Chỉ cách khác để phát hiện sự gián đoạn là để kiểm tra định kỳ tình trạng gián đoạn bằng cách gọi Thread.interrupted (). Phương pháp này thường được sử dụng để xây dựng và hiển thị các hộp thoại phức tạp, I / O quá trình dẫn đến sự thay đổi trạng thái thành phần (ví dụ như tải một tài liệu thành một phần văn bản), tải lớp chuyên sâu hay tính toán, chờ đợi cho các tin nhắn hoặc để thiết lập một kết nối mạng, vv tham khảo: Các thành viên của nhóm Swing đã viết một vài bài viết về việc sử dụng đề với Swing, và đã cung cấp một lớp được gọi là SwingWorker đó làm cho việc quản lý các loại hình đa luồng mô tả ở đây thuận tiện hơn. Xem http://java.sun.com/products/jfc/tsc/archive/tech_topics_arch/threads/threads.html 2.3.1 Trường hợp đặc biệt có một số trường hợp đặc biệt mà chúng ta không cần phải ủy đang ảnh hưởng đến trạng thái của các thành phần để event-dispatching: 1. Một số phương pháp trong Swing, mặc dù ít và xa giữa, được đánh dấu là thread-an toàn và không cần quan tâm đặc biệt. Một số phương pháp là thread-an toàn nhưng không được đánh dấu như vậy: repaint (), xác thực lại (), và vô hiệu (). 2. Một thành phần có thể được xây dựng và thao tác trong bất kỳ thời trang chúng tôi thích, mà không quan tâm cho chủ đề, miễn là nó vẫn chưa được thực hiện (tức là nó đã được hiển thị hoặc một yêu cầu vẽ lại đã được xếp hàng đợi). Container cấp cao (JFrame, JDialog, JApplet) được thực hiện sau khi các setVisible (true), show (), hoặc pack () đã được gọi vào chúng. Cũng lưu ý rằng một thành phần được coi là đã nhận ra ngay khi nó được đưa vào một container nhận ra. 3. Khi giao dịch với Swing applet (JApplets) tất cả các thành phần có thể được xây dựng và thao tác mà không liên quan đến chủ đề cho đến khi bắt đầu () phương pháp đã được gọi là, xảy ra sau khi các phương pháp init (). 2.3.2 Làm thế nào để chúng ta xây dựng của chúng ta thread-an toàn phương pháp? Điều này là khá dễ dàng. Dưới đây là một chủ đề an toàn phương pháp mẫu chúng ta có thể sử dụng để đảm bảo mã của phương pháp này chỉ thực hiện trong event-dispatching: public void doThreadSafeWork () { if (SwingUtilities.isEventDispatchThread ()) { // // làm tất cả công việc ở đây .. . // } else { Runnable callDoThreadSafeWork = new Runnable () { public void run () { doThreadSafeWork (); } }; SwingUtilities.invokeLater (callDoThreadSafeWork); } } 2.3.3 Làm thế nào để invokeLater () và invokeAndWait () làm việc? javax.swing.SystemEventQueueUtilities lớp [gói tin] Khi SwingUtilities nhận một đối tượng Runnable qua invokeLater (), nó vượt qua nó ngay lập tức để các postRunnable () phương thức của một lớp được gọi là SystemEventQueueUtilities. Nếu một Runnable nhận được thông qua invokeAndWait (), lần đầu tiên các chủ đề hiện tại đang kiểm tra để chắc chắn rằng nó không phải là event-dispatching. (Nó sẽ gây tử vong cho phép invokeAndWait () được gọi từ event-công văn chính nó!) Một lỗi xảy ra nếu điều này là trường hợp. Nếu không, chúng ta xây dựng một đối tượng sử dụng như là các khóa trên một phần quan trọng (ví dụ như một khối đồng bộ). Khối này chứa hai câu. Người đầu tiên gửi Runnable để SystemEventQueueUtilities 'phương pháp postRunnable (), cùng với một tham chiếu đến các đối tượng khóa. Việc chờ đợi thứ hai trên đối tượng khóa nên đang gọi sẽ không tiếp tục cho đến khi đối tượng này được thông báo - do đó "gọi và chờ đợi." The () phương pháp postRunnable đầu tiên giao tiếp với SystemEventQueue tin, một lớp bên trong của SystemEventQueueUtilities, để trả lại một tham chiếu đến hàng đợi sự kiện hệ thống. Chúng tôi sau đó quấn Runnable trong một thể hiện của RunnableEvent, một lớp bên trong nhân. Các nhà xây dựng RunnableEvent mất một Runnable và một đối tượng đại diện cho các đối tượng khóa (null nếu invokeLater () đã được gọi) là tham số. Các lớp RunnableEvent là một lớp con của AWTEvent, và định nghĩa ID static int sự kiện riêng của mình - event_id. (Lưu ý rằng bất cứ khi nào chúng ta định nghĩa sự kiện riêng của chúng tôi, chúng tôi dự kiến sẽ sử dụng một ID sự kiện lớn hơn giá trị của AWTEvent.RESERVED_ID_MAX.) Event_id RunnableEvent là AWTEvent.RESERVED_ID_MAX + 1000. RunnableEvent cũng có một trường hợp tĩnh của một RunnableTarget, nhưng một bên tin class. RunnableTarget là một lớp con của Hợp phần và mục đích duy nhất của nó là hoạt động như nguồn và mục tiêu của RunnableEvents. Làm thế nào để RunnableTarget làm điều này? Constructor của nó cho phép các sự kiện với ID sự kiện phù hợp với ID của RunnableEvent: enableEvents (RunnableEvent.EVENT_ID); Nó cũng đè processEvent bảo vệ () phương pháp của Hợp phần để nhận RunnableEvents. Bên trong phương pháp này nó kiểm tra trước để xem các sự kiện thông qua như là tham số thực chất là một thể hiện của RunnableEvent. Nếu có, nó sẽ chuyển qua SystemEventQueueUtilities 'processRunnableEvent () phương pháp (điều này xảy ra sau khi RunnableEvent đã được gửi đi từ hàng đợi sự kiện hệ thống.) Bây giờ trở lại RunnableEvent. Các nhà xây dựng RunnableEvent gọi của lớp cha (AWTEvent) constructor đi qua trường hợp tĩnh của RunnableTarget là nguồn sự kiện, và event_id như các ID sự kiện.
đang được dịch, vui lòng đợi..
