Διατηρώντας το νήμα Swing ασφαλές
Το τελευταίο βήμα για τη δημιουργία ενός Swing GUI είναι να το ξεκινήσετε. Ο σωστός τρόπος για να ξεκινήσετε ένα Swing GUI σήμερα διαφέρει από την αρχικά καθορισμένη προσέγγιση της Sun. Ακολουθεί και πάλι το απόσπασμα από την τεκμηρίωση της Sun:
Μόλις πραγματοποιηθεί μια συνάρτηση Swing, όλοι οι κωδικοί που ενδέχεται να επηρεάσουν ή να εξαρτηθούν από την κατάσταση αυτού του στοιχείου θα πρέπει να εκτελούνται στο νήμα αποστολής συμβάντων.Τώρα ρίξτε αυτές τις οδηγίες έξω από το παράθυρο, γιατί όταν κυκλοφόρησε το JSE 1.5 όλα τα παραδείγματα στον ιστότοπο της Sun άλλαξαν. Από τότε, είναι λίγο γνωστό το γεγονός ότι πρέπει πάντα προσπελάστε τα στοιχεία Swing στο νήμα αποστολής συμβάντων για να εξασφαλίσετε την ασφάλεια του νήματος / την πρόσβαση σε ένα νήμα. Ο λόγος πίσω από την αλλαγή είναι απλός: ενώ το πρόγραμμά σας ενδέχεται να έχει πρόσβαση σε ένα στοιχείο Swing από το νήμα αποστολής συμβάντων προτού πραγματοποιηθεί το στοιχείο, η αρχικοποίηση του Swing UI θα μπορούσε να προκαλέσει κάτι να τρέξει στο νήμα αποστολής συμβάντων μετά, επειδή το Το στοιχείο / UI αναμένει να εκτελέσει τα πάντα στο νήμα αποστολής συμβάντων. Η εκτέλεση στοιχείων GUI σε διαφορετικά νήματα σπάζει το μονόστροφο μοντέλο προγραμματισμού της Swing.
Το πρόγραμμα στη λίστα 5 δεν είναι αρκετά ρεαλιστικό, αλλά χρησιμεύει για να επισημάνω.
Λίστα 5. Πρόσβαση στην κατάσταση στοιχείων Swing από πολλά νήματα
εισαγωγή java.awt. *; εισαγωγή java.awt.event. *; εισαγωγή javax.swing. *; δημόσια τάξη BadSwingButton {public static void main (String args []) {JFrame frame = new JFrame ("Τίτλος"); frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); Κουμπί JButton = νέο JButton ("Πατήστε εδώ"); ContainerListener container = νέο ContainerAdapter () {public void componentAdded (final ContainerEvent e) {SwingWorker worker = new SwingWorker () {προστατευμένο String doInBackground () ρίχνει InterruptException {Thread.sleep (250); επιστροφή μηδέν; } προστατευμένο κενό έγινε () {System.out.println ("Στο νήμα του συμβάντος::" + EventQueue.isDispatchThread ()); Κουμπί JButton = (JButton) e.getChild (); String label = button.getText (); button.setText (ετικέτα + "0"); }} worker.execute (); }} frame.getContentPane (). addContainerListener (κοντέινερ); frame.add (κουμπί, BorderLayout.CENTER); frame.setSize (200, 200); δοκιμάστε το {Thread.sleep (500); } catch (InterruptException e) {} System.out.println ("Πρόκειται να πραγματοποιηθεί:" + EventQueue.isDispatchThread ()); frame.setVisible (true); }}
Παρατηρήστε ότι η έξοδος εμφανίζει κάποιο κώδικα που εκτελείται στο κύριο νήμα πριν από την πραγματοποίηση της διεπαφής χρήστη. Αυτό σημαίνει ότι ο κωδικός αρχικοποίησης εκτελείται σε ένα νήμα, ενώ άλλος κωδικός διεπαφής χρήστη εκτελείται στο νήμα αποστολής συμβάντων, το οποίο σπάει το μονόκλωνο μοντέλο πρόσβασης της Swing
> java BadSwingButton Στο νήμα της εκδήλωσης; : αλήθεια πρόκειται να συνειδητοποιήσω: λάθος
Το πρόγραμμα στην Λίστα 5 θα ενημερώσει την ετικέτα του κουμπιού από τον ακροατή κοντέινερ όταν το κουμπί προστεθεί στο κοντέινερ. Για να κάνετε το σενάριο πιο ρεαλιστικό, φανταστείτε ένα περιβάλλον χρήστη που "μετράει" ετικέτες σε αυτό και χρησιμοποιεί το πλήθος ως κείμενο στον τίτλο περιγράμματος. Φυσικά, θα χρειαστεί να ενημερώσετε το κείμενο του περιγράμματος του περιγράμματος στο νήμα αποστολής συμβάντων. Για να απλοποιήσετε τα πράγματα, το πρόγραμμα ενημερώνει απλώς την ετικέτα ενός κουμπιού. Αν και δεν είναι ρεαλιστικό σε λειτουργία, αυτό το πρόγραμμα δείχνει το πρόβλημα με κάθε Πρόγραμμα Swing που έχει γραφτεί από την αρχή της εποχής του Swing. (Ή τουλάχιστον όλα αυτά που ακολούθησαν το προτεινόμενο νήμα που βρέθηκε στα javadocs και σε απευθείας σύνδεση σεμινάρια από την Sun Microsystems, ακόμα και στις δικές μου πρώτες εκδόσεις βιβλίων προγραμματισμού Swing.)
Η αλλαγή σπειρώματος έγινε σωστά
Ο τρόπος για να κάνετε το Swing threading σωστά είναι να ξεχάσετε την αρχική υπαγόρευση της Sun. Μην ανησυχείτε για το εάν ένα στοιχείο πραγματοποιείται ή όχι. Μην ενοχλείτε να προσπαθήσετε να προσδιορίσετε εάν είναι ασφαλές να αποκτήσετε πρόσβαση σε κάτι από το νήμα αποστολής συμβάντων. Δεν είναι ποτέ. Αντ 'αυτού, δημιουργήστε ολόκληρο το περιβάλλον χρήστη στο νήμα αποστολής συμβάντων. Εάν πραγματοποιήσετε ολόκληρη την κλήση δημιουργίας διεπαφής χρήστη μέσα σε ένα EventQueue.invokeLater ()
όλες οι προσβάσεις κατά την προετοιμασία είναι εγγυημένες ότι θα γίνουν στο νήμα αποστολής συμβάντων. Είναι τόσο απλό.
Λίστα 6. Τα πάντα στη θέση του
εισαγωγή java.awt. *; εισαγωγή java.awt.event. *; εισαγωγή javax.swing. *; δημόσια τάξη GoodSwingButton {public static void main (String args []) {Runnable runner = new Runnable () {public void run () {JFrame frame = new JFrame ("Τίτλος"); frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); Κουμπί JButton = νέο JButton ("Πατήστε εδώ"); ContainerListener container = new ContainerAdapter () {public void componentAdded (final ContainerEvent e) {SwingWorker worker = new SwingWorker () {προστατευμένο String doInBackground () ρίχνει το InterruptException {return null; } προστατευμένο κενό έγινε () {System.out.println ("Στο νήμα του συμβάντος::" + EventQueue.isDispatchThread ()); Κουμπί JButton = (JButton) e.getChild (); String label = button.getText (); button.setText (ετικέτα + "0"); }} worker.execute (); }} frame.getContentPane (). addContainerListener (κοντέινερ); frame.add (κουμπί, BorderLayout.CENTER); frame.setSize (200, 200); System.out.println ("Πρόκειται να πραγματοποιήσω:" + EventQueue.isDispatchThread ()); frame.setVisible (true); }} EventQueue.invokeLater (δρομέας); }}
Εκτελέστε το τώρα και το παραπάνω πρόγραμμα θα δείξει ότι τόσο η αρχικοποίηση όσο και ο κώδικας κοντέινερ εκτελούνται στο νήμα αποστολής συμβάντων:
> java GoodSwingButton Πρόκειται να συνειδητοποιήσω: αλήθεια Στο νήμα της εκδήλωσης; : αλήθεια
Συμπερασματικά
Η επιπλέον εργασία για τη δημιουργία της διεπαφής χρήστη στο νήμα αποστολής συμβάντων μπορεί να φαίνεται περιττή στην αρχή. Όλοι το έχουν κάνει το αντίθετο από την αρχή του χρόνου, τελικά. Γιατί να ασχοληθείτε με την αλλαγή τώρα; Το θέμα είναι ότι το κάναμε πάντα λάθος. Για να διασφαλιστεί η σωστή πρόσβαση στα στοιχεία Swing, θα πρέπει πάντα να δημιουργείτε ολόκληρο το περιβάλλον χρήστη στο νήμα αποστολής συμβάντων, όπως φαίνεται εδώ:
Runnable runner = new Runnable () {public void run () {// ... create UI εδώ ...}} EventQueue.invokeLater (runner);
Η μετακίνηση του κώδικα αρχικοποίησης στο νήμα αποστολής συμβάντων είναι ο μόνος τρόπος για να διασφαλίσετε ότι τα Swing GUIs σας είναι ασφαλή στο νήμα. Ναι, θα νιώθει άβολα στην αρχή, αλλά η πρόοδος συνήθως συμβαίνει.
Ο John Zukowski παίζει με την Java για πάνω από 12 χρόνια τώρα, έχοντας εγκαταλείψει τη νοοτροπία C και X-Windows εδώ και πολύ καιρό. Με 10 βιβλία για θέματα από το Swing έως τις συλλογές στο Java SE 6, ο John πραγματοποιεί πλέον συμβουλευτική στρατηγικής τεχνολογίας μέσω της επιχείρησής του, JZ Ventures, Inc.Μάθετε περισσότερα σχετικά με αυτό το θέμα
- Μάθετε περισσότερα σχετικά με τον προγραμματισμό Swing και το νήμα αποστολής συμβάντων από έναν από τους κύριους προγραμματιστές ανάπτυξης Java: Chet Haase σχετικά με τη μεγιστοποίηση του Swing και του Java 2D (JavaWorld Java Technology Insider podcast, Αύγουστος 2007).
- "Προσαρμόστε το SwingWorker για να βελτιώσετε τα Swing GUIs" (Yexin Chen, JavaWorld, Ιούνιος 2003) σκάβει βαθύτερα σε μερικές από τις προκλήσεις του Swing threading που συζητούνται σε αυτό το άρθρο και εξηγεί πώς μια προσαρμοσμένη
SwingWorker
μπορεί να παρέχει στους μυς να δουλεύουν γύρω τους. - Ο "χειρισμός Java και Event" (Todd Sundsted, JavaWorld, Αύγουστος 1996) είναι ένας εκκινητής στο χειρισμό εκδηλώσεων γύρω από το AWT.
- Το "Speed up listener notification" (Robert Hastings, JavaWorld, Φεβρουάριος 2000) εισάγει την προδιαγραφή JavaBeans 1.0 για εγγραφή και ειδοποίηση συμβάντων.
- "Επίτευξη ισχυρής απόδοσης με νήματα, Μέρος 1" (Jeff Friesen, JavaWorld, Μάιος 2002) παρουσιάζει τα νήματα Java. Ανατρέξτε στο Μέρος 2 για απάντηση στην ερώτηση: Γιατί χρειαζόμαστε συγχρονισμό;
- "Η εκτέλεση εργασιών σε νήματα" είναι ένα απόσπασμα JavaWorld από Java Concurrency in Practice (Brian Goetz, et al., Addison Wesley Professional, Μάιος 2006) που ενθαρρύνει τον προγραμματισμό νήματος βάσει εργασιών και εισάγει ένα πλαίσιο εκτέλεσης για τη διαχείριση εργασιών.
- Το "Threads and Swing" (Hans Muller και Kathy Walrath, Απρίλιος 1998) είναι μια από τις πρώτες επίσημες αναφορές για το Swing threading. Περιλαμβάνει τον πλέον διάσημο (και εσφαλμένο) "κανόνα ενός νήματος".
- Η δημιουργία ενός GUI με JFC / Swing είναι η περιεκτική σελίδα Java Tutorial για τον προγραμματισμό Swing GUI.
- Το "Concurrency in Swing" είναι ένα σεμινάριο για το Swing trail που περιλαμβάνει μια εισαγωγή στο
SwingWorker
τάξη. - JSR 296: Το Swing Application Framework είναι προς το παρόν μια προδιαγραφή σε εξέλιξη. Δείτε επίσης "Χρήση του Swing Application Framework" (John O'Conner, Sun Developer Network, Ιούλιος 2007) για να μάθετε περισσότερα σχετικά με αυτό το επόμενο βήμα στην εξέλιξη του Swing GUI προγραμματισμού.
- Ολόκληρη η αναφορά Java AWT (John Zukowski, O'Reilly, Μάρτιος 1997) διατίθεται δωρεάν από τον ηλεκτρονικό κατάλογο O'Reilly.
- Ο John Definitive Guide to Java Swing, Third Edition (Apress, June 2005) έχει ενημερωθεί πλήρως για την Java Standard Edition έκδοση 5.0. Διαβάστε εδώ ένα κεφάλαιο προεπισκόπησης από το βιβλίο JavaWorld!
- Επισκεφτείτε το ερευνητικό κέντρο JavaWorld Swing / GUI για περισσότερα άρθρα σχετικά με τον προγραμματισμό Swing και την ανάπτυξη της επιφάνειας εργασίας Java.
- Ελέγξτε επίσης τα φόρουμ προγραμματιστών JavaWorld για συζητήσεις και ερωτήσεις και απαντήσεις σχετικά με τον προγραμματισμό Swing και Java.
Αυτή η ιστορία, "Swing threading and the event-dispatch thread" δημοσιεύθηκε αρχικά από την JavaWorld.