Threading swing e thread di invio di eventi

Precedente 1 2 3 4 5 Pagina 5 Pagina 5 di 5

Mantenere il thread Swing al sicuro

L'ultimo passaggio nella creazione di una GUI Swing è avviarlo. Il modo corretto per avviare oggi una GUI Swing è diverso dall'approccio originariamente prescritto da Sun. Ecco di nuovo la citazione dalla documentazione di Sun:

Una volta che un componente Swing è stato realizzato, tutto il codice che potrebbe influenzare o dipendere dallo stato di quel componente dovrebbe essere eseguito nel thread di invio degli eventi.

Ora lanciate quelle istruzioni fuori dalla finestra, perché quando è stato rilasciato JSE 1.5 tutti gli esempi sul sito di Sun sono cambiati. Da quel momento è stato un fatto poco noto che dovresti sempre accedere ai componenti Swing sul thread di invio degli eventi per garantire la loro sicurezza del thread / accesso a thread singolo. Il motivo alla base della modifica è semplice: mentre il tuo programma potrebbe accedere a un componente Swing al di fuori del thread di invio degli eventi prima che il componente venga realizzato, l'inizializzazione dell'interfaccia utente di Swing potrebbe attivare qualcosa da eseguire sul thread di invio dell'evento in seguito, perché component / UI si aspetta di eseguire tutto sul thread di invio degli eventi. Avere componenti GUI eseguiti su thread diversi interrompe il modello di programmazione a thread singolo di Swing.

Il programma nel Listato 5 non è del tutto realistico, ma serve a chiarire il mio punto.

Listato 5. Accesso allo stato del componente Swing da più thread

import java.awt.*; import java.awt.event.*; import javax.swing.*; public class BadSwingButton { public static void main(String args[]) { JFrame frame = new JFrame("Title"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton button = new JButton("Press Here"); ContainerListener container = new ContainerAdapter() { public void componentAdded(final ContainerEvent e) { SwingWorker worker = new SwingWorker() { protected String doInBackground() throws InterruptedException { Thread.sleep(250); return null; } protected void done() { System.out.println("On the event thread? : " + EventQueue.isDispatchThread()); JButton button = (JButton)e.getChild(); String label = button.getText(); button.setText(label + "0"); } }; worker.execute(); } }; frame.getContentPane().addContainerListener(container); frame.add(button, BorderLayout.CENTER); frame.setSize(200, 200); try { Thread.sleep(500); } catch (InterruptedException e) { } System.out.println("I'm about to be realized: " + EventQueue.isDispatchThread()); frame.setVisible(true); } }

Si noti che l'output mostra del codice in esecuzione sul thread principale prima della realizzazione dell'interfaccia utente. Ciò significa che il codice di inizializzazione è in esecuzione su un thread mentre altro codice dell'interfaccia utente è in esecuzione sul thread di invio degli eventi, il che interrompe il modello di accesso a thread singolo di Swing:

> java BadSwingButton On the event thread? : true I'm about to be realized: false

Il programma nel Listato 5 aggiornerà l'etichetta del pulsante dal listener del contenitore quando il pulsante viene aggiunto al contenitore. Per rendere lo scenario più realistico, immagina un'interfaccia utente che "conti" le etichette al suo interno e utilizzi il conteggio come testo nel titolo del bordo. Naturalmente, dovrebbe aggiornare il testo del titolo del bordo nel thread di invio dell'evento. Per mantenere le cose semplici, il programma aggiorna semplicemente l'etichetta di un pulsante. Sebbene non sia realistico nella funzione, questo programma mostra il problema con ogni programma Swing che è stato scritto dall'inizio dei tempi di Swing. (O almeno tutti quelli che hanno seguito il modello di threading consigliato trovato nei javadoc e nei tutorial online di Sun Microsystems, e anche nelle mie prime edizioni dei libri di programmazione Swing.)

Filettatura dell'oscillazione eseguita correttamente

Il modo per ottenere il threading Swing corretto è dimenticare il detto originale di Sun. Non preoccuparti se un componente è realizzato o meno. Non preoccuparti di provare a determinare se è sicuro accedere a qualcosa al di fuori del thread di invio degli eventi. Non lo è mai. Crea invece l'intera interfaccia utente sul thread di invio degli eventi. Se si inserisce l'intera chiamata di creazione dell'interfaccia utente all'interno di un EventQueue.invokeLater()tutti gli accessi durante l'inizializzazione, è garantito che vengano eseguiti nel thread di invio degli eventi. È così semplice.

Listato 6. Tutto al suo posto

import java.awt.*; import java.awt.event.*; import javax.swing.*; public class GoodSwingButton { public static void main(String args[]) { Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("Title"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton button = new JButton("Press Here"); ContainerListener container = new ContainerAdapter() { public void componentAdded(final ContainerEvent e) { SwingWorker worker = new SwingWorker() { protected String doInBackground() throws InterruptedException { return null; } protected void done() { System.out.println("On the event thread? : " + EventQueue.isDispatchThread()); JButton button = (JButton)e.getChild(); String label = button.getText(); button.setText(label + "0"); } }; worker.execute(); } }; frame.getContentPane().addContainerListener(container); frame.add(button, BorderLayout.CENTER); frame.setSize(200, 200); System.out.println("I'm about to be realized: " + EventQueue.isDispatchThread()); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }

Eseguilo ora e il programma sopra mostrerà che sia l'inizializzazione che il codice del contenitore sono in esecuzione sul thread di invio dell'evento:

> java GoodSwingButton I'm about to be realized: true On the event thread? : true

In conclusione

All'inizio il lavoro extra per creare la tua interfaccia utente nel thread di invio degli eventi potrebbe sembrare inutile. Dopotutto, tutti lo hanno fatto in un altro modo dall'inizio dei tempi. Perché preoccuparsi di cambiare adesso? Il fatto è che abbiamo sempre sbagliato. Per assicurarti che i tuoi componenti Swing siano accessibili correttamente, dovresti sempre creare l'intera interfaccia utente nel thread di invio degli eventi, come mostrato qui:

Runnable runner = new Runnable() { public void run() { // ...create UI here... } } EventQueue.invokeLater(runner);

Spostare il codice di inizializzazione nel thread di invio dell'evento è l'unico modo per garantire che le GUI Swing siano thread-safe. Sì, all'inizio sembrerà imbarazzante, ma di solito il progresso sì.

John Zukowski gioca con Java da oltre 12 anni ormai, avendo abbandonato la sua mentalità C e X-Windows molto tempo fa. Con 10 libri pubblicati su argomenti da Swing a collezioni a Java SE 6, John ora offre consulenza tecnologica strategica attraverso la sua azienda, JZ Ventures, Inc ..

Ulteriori informazioni su questo argomento

  • Ulteriori informazioni sulla programmazione Swing e sul thread di invio di eventi da uno dei maestri dello sviluppo di desktop Java: Chet Haase sulla massimizzazione di Swing e Java 2D (podcast JavaWorld Java Technology Insider, agosto 2007).
  • "Personalizza SwingWorker per migliorare Swing GUI" (Yexin Chen, JavaWorld, giugno 2003) approfondisce alcune delle sfide del threading Swing discusse in questo articolo e spiega come un customizzato SwingWorkerpuò fornire i muscoli per aggirarle.
  • "Java and event handling" (Todd Sundsted, JavaWorld, August 1996) è un manuale di base sulla gestione degli eventi circa AWT.
  • "Accelerare la notifica del listener" (Robert Hastings, JavaWorld, febbraio 2000) introduce la specifica JavaBeans 1.0 per la registrazione e la notifica degli eventi.
  • "Ottieni prestazioni elevate con i thread, parte 1" (Jeff Friesen, JavaWorld, maggio 2002) introduce i thread Java. Vedere la parte 2 per una risposta alla domanda: perché è necessaria la sincronizzazione?
  • "Esecuzione di attività nei thread" è un estratto JavaWorld da Java Concurrency in Practice (Brian Goetz, et al., Addison Wesley Professional, maggio 2006) che incoraggia la programmazione di thread basata su attività e introduce un framework di esecuzione per la gestione delle attività.
  • "Threads and Swing" (Hans Muller e Kathy Walrath, aprile 1998) è uno dei primi riferimenti ufficiali per il threading Swing. Include la ormai famosa (ed errata) "regola a thread singolo".
  • La creazione di una GUI con JFC / Swing è la pagina completa del tutorial Java per la programmazione della GUI Swing.
  • "Concurrency in Swing" è un tutorial sul percorso Swing che include un'introduzione alla SwingWorkerclasse.
  • JSR 296: Swing Application Framework è attualmente una specifica in corso. Vedere anche "Using the Swing Application Framework" (John O'Conner, Sun Developer Network, luglio 2007) per saperne di più su questo passo successivo nell'evoluzione della programmazione Swing GUI.
  • L'intero Java AWT Reference (John Zukowski, O'Reilly, marzo 1997) è disponibile gratuitamente nel catalogo in linea O'Reilly.
  • John's Definitive Guide to Java Swing, Third Edition (Apress, June 2005) è completamente aggiornato per Java Standard Edition versione 5.0. Leggi un capitolo in anteprima dal libro proprio qui su JavaWorld !
  • Visita il centro di ricerca JavaWorld Swing / GUI per ulteriori articoli sulla programmazione Swing e lo sviluppo di desktop Java.
  • Controlla anche i forum degli sviluppatori JavaWorld per discussioni e domande e risposte relative a Swing e alla programmazione desktop Java.

Questa storia, "Swing threading and the event-dispatch thread" è stata originariamente pubblicata da JavaWorld.