Programmering

Swing-threading og event-forsendelsestråden

Forrige 1 2 3 4 5 Side 5 Side 5 af 5

Opbevaring af svingtråd

Det sidste trin i oprettelsen af ​​en Swing GUI er at starte den op. Den korrekte måde at starte en Swing GUI på i dag adskiller sig fra Suns oprindeligt foreskrevne tilgang. Her er citatet fra Sun-dokumentationen igen:

Når en Swing-komponent er blevet realiseret, skal al kode, der kan påvirke eller afhænge af den pågældende komponents tilstand, udføres i hændelsesforsendelsestråden.

Kast nu disse instruktioner ud af vinduet, for omkring da JSE 1.5 blev frigivet ændrede alle eksemplerne på Suns websted. Siden den tid har det været lidt kendt, at du skulle altid få adgang til svingkomponenter på begivenhedsforsendelsestråden for at sikre deres trådsikkerhed / enkeltgevindadgang. Årsagen til ændringen er enkel: Selvom dit program muligvis får adgang til en Swing-komponent fra begivenhedsforsendelsestråden, før komponenten realiseres, kan initialiseringen af ​​Swing UI udløse noget til at køre på begivenhedsforsendelsestråden bagefter, fordi komponent / brugergrænseflade forventer at køre alt på begivenhedsforsendelsestråden. At have GUI-komponenter kørt på forskellige tråde, bryder Swings enkelttrådede programmeringsmodel.

Programmet i Listing 5 er ikke helt realistisk, men det tjener til at gøre min pointe.

Listing 5. Adgang til Swing-komponenttilstand fra flere tråde

import java.awt. *; import java.awt.event. *; import javax.swing. *; offentlig klasse BadSwingButton {public static void main (String args []) {JFrame frame = new JFrame ("Title"); frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); JButton-knap = ny JButton ("Tryk her"); ContainerListener container = ny ContainerAdapter () {public void componentAdded (final ContainerEvent e) {SwingWorker worker = new SwingWorker () {protected String doInBackground () throw InterruptedException {Thread.sleep (250); returnere null; } beskyttet ugyldigt gjort () {System.out.println ("På begivenhedstråden?:" + EventQueue.isDispatchThread ()); JButton-knap = (JButton) e.getChild (); String label = button.getText (); button.setText (label + "0"); }}; arbejder.exe (); }}; frame.getContentPane (). addContainerListener (container); frame.add (knap, BorderLayout.CENTER); frame.setSize (200, 200); prøv {Thread.sleep (500); } fange (InterruptedException e) {} System.out.println ("Jeg er ved at blive realiseret:" + EventQueue.isDispatchThread ()); frame.setVisible (true); }}

Bemærk, at output viser noget kode, der kører på hovedtråden, inden UI'et realiseres. Dette betyder, at initialiseringskoden kører på en tråd, mens anden UI-kode kører på begivenhedsforsendelsestråden, hvilket bryder Swings enkelttrådede adgangsmodel:

> java BadSwingButton På begivenhedstråden? : sandt Jeg er ved at blive realiseret: falsk

Programmet i Listing 5 opdaterer knapens etiket fra containerlytteren, når knappen føjes til containeren. For at gøre scenariet mere realistisk, forestil dig et brugergrænseflade, der "tæller" etiketter i det og bruger optællingen som teksten i grænsetitlen. Naturligvis ville det være nødvendigt at opdatere grænsens titeltekst i hændelsesforsendelsestråden. For at gøre tingene enkle opdaterer programmet bare etiketten på en knap. Selvom det ikke er realistisk i funktion, viser dette program problemet med hver Swing-program, der er skrevet siden begyndelsen af ​​Swings tid. (Eller i det mindste alle dem, der fulgte den anbefalede threading-model, der findes i javadocs og online-tutorials fra Sun Microsystems og endda i mine helt egne tidlige udgaver af Swing-programmeringsbøger.)

Swing threading gjort rigtigt

Måden at få Swing-trådning til højre er at glemme Suns originale dictum. Vær ikke bekymret for, om en komponent er realiseret eller ej. Lad være med at forsøge at afgøre, om det er sikkert at få adgang til noget fra begivenhedens afsendelsestråd. Det er det aldrig. Opret i stedet hele brugergrænsefladen på hændelsesforsendelsestråden. Hvis du placerer hele UI-oprettelsesopkaldet inde i et EventQueue.invokeLater () alle adganger under initialisering er garanteret at blive udført i hændelsesforsendelsestråden. Det er så simpelt.

Liste 6. Alt på plads

import java.awt. *; import java.awt.event. *; import javax.swing. *; offentlig klasse 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-knap = ny JButton ("Tryk her"); ContainerListener container = ny ContainerAdapter () {public void componentAdded (final ContainerEvent e) {SwingWorker worker = new SwingWorker () {protected String doInBackground () throw InterruptedException {return null; } beskyttet ugyldigt gjort () {System.out.println ("På begivenhedstråden?:" + EventQueue.isDispatchThread ()); JButton-knap = (JButton) e.getChild (); String label = button.getText (); button.setText (label + "0"); }}; arbejder.exe (); }}; frame.getContentPane (). addContainerListener (container); frame.add (knap, BorderLayout.CENTER); frame.setSize (200, 200); System.out.println ("Jeg er ved at blive realiseret:" + EventQueue.isDispatchThread ()); frame.setVisible (true); }}; EventQueue.invokeLater (løber); }}

Kør det nu, og ovenstående program viser, at både initialiseringen og containerkoden kører på hændelsesforsendelsestråden:

> java GoodSwingButton Jeg er ved at blive realiseret: sand På begivenhedstråden? : rigtigt

Afslutningsvis

Det ekstra arbejde med at oprette dit brugergrænseflade i hændelsesforsendelsestråden kan synes unødvendigt i starten. Alle har trods alt gjort det på den anden måde. Hvorfor gider at ændre nu? Sagen er, at vi altid har gjort det forkert. For at sikre, at dine Swing-komponenter fås korrekt, skal du altid oprette hele brugergrænsefladen i hændelsesforsendelsestråden som vist her:

Runnable runner = new Runnable () {public void run () {// ... Opret brugergrænseflade her ...}} EventQueue.invokeLater (runner);

Flytning af din initialiseringskode til begivenhedsforsendelsestråden er den eneste måde at sikre, at dine Swing GUI'er er trådsikre. Ja, det vil føles akavet i starten, men fremskridt gør det normalt.

John Zukowski har spillet med Java i godt over 12 år nu efter at have forladt sin C- og X-Windows-tankegang for længe siden. Med 10 bøger ude om emner fra gynge til samlinger til Java SE 6, udfører John nu strategisk teknologirådgivning gennem sin virksomhed, JZ Ventures, Inc.

Lær mere om dette emne

  • Lær mere om Swing-programmering og begivenhedsforsendelsestråden fra en af ​​mestrene i Java desktop-udvikling: Chet Haase om maksimering af Swing og Java 2D (JavaWorld Java Technology Insider podcast, august 2007).
  • "Tilpas SwingWorker for at forbedre Swing GUI'er" (Yexin Chen, JavaWorld, juni 2003) graver dybere ned i nogle af de Swing-threading-udfordringer, der diskuteres i denne artikel og forklarer, hvordan en SwingWorker kan give musklen til at arbejde omkring dem.
  • "Java og begivenhedshåndtering" (Todd Sundsted, JavaWorld, august 1996) er en primer på begivenhedshåndtering omkring AWT.
  • "Fremskynde lytternotifikation" (Robert Hastings, JavaWorld, februar 2000) introducerer JavaBeans 1.0-specifikationen til hændelsesregistrering og underretning.
  • "Opnå stærk ydeevne med tråde, del 1" (Jeff Friesen, JavaWorld, maj 2002) introducerer Java-tråde. Se del 2 for et svar på spørgsmålet: Hvorfor har vi brug for synkronisering?
  • "Udførelse af opgaver i tråde" er et JavaWorld-uddrag fra Java-samtidighed i praksis (Brian Goetz, et al., Addison Wesley Professional, maj 2006), der tilskynder til opgavebaseret trådprogrammering og introducerer en eksekveringsramme for opgavestyring.
  • "Tråde og sving" (Hans Muller og Kathy Walrath, april 1998) er en af ​​de tidligste officielle referencer for gyngetrådning. Det inkluderer den nu berømte (og fejlagtige) "single-thread rule".
  • Oprettelse af en GUI med JFC / Swing er den omfattende Java Tutorial-side til Swing GUI-programmering.
  • "Concurrency in Swing" er en tutorial om Swing trail, der inkluderer en introduktion til SwingWorker klasse.
  • JSR 296: Swing Application Framework er i øjeblikket en specifikation i gang. Se også "Brug af Swing Application Framework" (John O'Conner, Sun Developer Network, juli 2007) for at lære mere om dette næste trin i udviklingen af ​​Swing GUI-programmering.
  • Hele Java AWT Reference (John Zukowski, O'Reilly, marts 1997) er tilgængelig gratis fra O'Reilly Online Catalog.
  • Johns definitive guide til Java Swing, tredje udgave (Apress, juni 2005) er fuldstændig opdateret til Java Standard Edition version 5.0. Læs et eksempelkapitel fra bogen lige her på JavaWorld!
  • Besøg JavaWorld Swing / GUI-forskningscenter for flere artikler om Swing-programmering og Java-desktopudvikling.
  • Tjek også JavaWorld-udviklerforaene for diskussioner og Q&A relateret til Swing og Java desktop programmering.

Denne historie, "Swing threading and the event-dispatch thread" blev oprindeligt udgivet af JavaWorld.