Προγραμματισμός

Java 101: Κατανόηση νημάτων Java, Μέρος 4: Ομάδες νημάτων, μεταβλητότητα και τοπικές μεταβλητές

Αυτό το μήνα Java 101 ολοκληρώνει τη σειρά νήματος εστιάζοντας σε ομάδες νημάτων, μεταβλητότητα, μεταβλητές τοπικού νήματος, χρονόμετρα και το Νήμα τάξη.

Κατανόηση των νημάτων Java - διαβάστε ολόκληρη τη σειρά

  • Μέρος 1: Παρουσίαση νημάτων και runnables
  • Μέρος 2: Συγχρονισμός νημάτων
  • Μέρος 3: Προγραμματισμός νημάτων, αναμονή / ειδοποίηση και διακοπή νημάτων
  • Μέρος 4: Ομάδες νημάτων, μεταβλητότητα, μεταβλητές τοπικού νήματος, χρονόμετρα και θάνατος νήματος

Ομάδες νημάτων

Σε ένα πρόγραμμα διακομιστή δικτύου, ένα νήμα περιμένει και δέχεται αιτήματα από προγράμματα πελατών για εκτέλεση, για παράδειγμα, συναλλαγές βάσης δεδομένων ή σύνθετους υπολογισμούς. Το νήμα δημιουργεί συνήθως ένα νέο νήμα για να χειριστεί το αίτημα. Ανάλογα με τον όγκο των αιτημάτων, ενδέχεται να υπάρχουν ταυτόχρονα πολλά διαφορετικά νήματα, περιπλέκοντας τη διαχείριση του νήματος. Για την απλοποίηση της διαχείρισης νήματος, τα προγράμματα οργανώνουν τα νήματά τους με ομάδες νημάτωνjava.lang.ThreadGroup αντικείμενα που ομαδοποιούν σχετικά νήματα Νήμα (και Νήμα subclass) αντικείμενα. Για παράδειγμα, το πρόγραμμά σας μπορεί να χρησιμοποιήσει ThreadGroup για να ομαδοποιήσετε όλα τα νήματα εκτύπωσης σε μία ομάδα.

Σημείωση: Για να διατηρήσω τη συζήτηση απλή, αναφέρομαι σε ομάδες νήματος σαν να οργανώνουν νήματα. Στην πραγματικότητα, οργανώνονται ομάδες νημάτων Νήμα (και Νήμα subclass) αντικείμενα που σχετίζονται με νήματα.

Η Java απαιτεί κάθε νήμα και κάθε ομάδα νήματος — αποθηκεύστε την ομάδα νήματος ρίζας, Σύστημα—Για συμμετοχή σε κάποια άλλη ομάδα νήματος. Αυτή η διάταξη οδηγεί σε μια ιεραρχική δομή ομάδας νήματος, την οποία απεικονίζει το παρακάτω σχήμα σε ένα πλαίσιο εφαρμογής.

Στην κορυφή της δομής του σχήματος είναι το Σύστημα νήμα ομάδα. Το JVM δημιουργήθηκε Σύστημα Η ομάδα οργανώνει νήματα JVM που ασχολούνται με την οριστικοποίηση αντικειμένων και άλλες εργασίες συστήματος και χρησιμεύει ως η ομάδα νήματος ρίζας της ιεραρχικής δομής της ομάδας νήματος μιας εφαρμογής. Ακριβώς από κάτω Σύστημα είναι το JVM-δημιουργήθηκε κύριος νήμα ομάδα, που είναι Σύστημαομάδα subthread (υποομάδα, για συντομία). κύριος περιέχει τουλάχιστον ένα νήμα - το κύριο νήμα που δημιουργήθηκε από το JVM και εκτελεί οδηγίες byte-code στο κύριος() μέθοδος.

Κάτω από κύριος η ομάδα κατοικεί το υποομάδα 1 και υποομάδα 2 υποομάδες, υποομάδες που δημιουργούνται από εφαρμογές (τις οποίες δημιουργεί η εφαρμογή του σχήματος). Επί πλέον, υποομάδα 1 ομαδοποιεί τρία νήματα που έχουν δημιουργηθεί από εφαρμογές: νήμα 1, νήμα 2, και νήμα 3. Σε αντίθεση, υποομάδα 2 ομαδοποιεί ένα νήμα που δημιουργήθηκε από την εφαρμογή: το νήμα μου.

Τώρα που γνωρίζετε τα βασικά, ας αρχίσουμε να δημιουργούμε ομάδες νήματος.

Δημιουργήστε ομάδες νήματος και συσχετίστε νήματα με αυτές τις ομάδες

ο ThreadGroup Η τεκμηρίωση SDK της κατηγορίας αποκαλύπτει δύο κατασκευαστές: ThreadGroup (Όνομα συμβολοσειράς) και ThreadGroup (γονέας ThreadGroup, όνομα συμβολοσειράς). Και οι δύο κατασκευαστές δημιουργούν μια ομάδα νήματος και της δίνουν ένα όνομα, ως όνομα η παράμετρος καθορίζει. Οι κατασκευαστές διαφέρουν στην επιλογή τους για το ποια ομάδα νήματος χρησιμεύει ως γονέας από την ομάδα νήματος που δημιουργήθηκε πρόσφατα. Κάθε ομάδα νήματος, εκτός Σύστημα, πρέπει να έχει μια ομάδα γονικών νημάτων. Για ThreadGroup (Όνομα συμβολοσειράς), ο γονέας είναι η ομάδα νήματος του νήματος που καλεί ThreadGroup (Όνομα συμβολοσειράς). Για παράδειγμα, εάν το κύριο νήμα καλεί ThreadGroup (Όνομα συμβολοσειράς), η ομάδα νήματος που δημιουργήθηκε πρόσφατα έχει την κύρια ομάδα του νήματος ως μητρική της—κύριος. Για ThreadGroup (γονέας ThreadGroup, όνομα συμβολοσειράς), ο γονέας είναι η ομάδα που μητρική εταιρεία βιβλιογραφικές αναφορές. Ο παρακάτω κώδικας δείχνει πώς να χρησιμοποιήσετε αυτούς τους κατασκευαστές για να δημιουργήσετε ένα ζεύγος ομάδων νημάτων:

public static void main (String [] args) {ThreadGroup tg1 = νέο ThreadGroup ("A"); ThreadGroup tg2 = νέο ThreadGroup (tg1, "B"); }

Στον παραπάνω κώδικα, το κύριο νήμα δημιουργεί δύο ομάδες νημάτων: ΕΝΑ και σι. Πρώτον, δημιουργείται το κύριο νήμα ΕΝΑ τηλεφωνώντας ThreadGroup (Όνομα συμβολοσειράς). ο tg1- ο γονέας της ομάδας νημάτων αναφοράς κύριος επειδή κύριος είναι η βασική ομάδα του νήματος. Δεύτερον, δημιουργείται το κύριο νήμα σι τηλεφωνώντας ThreadGroup (γονέας ThreadGroup, όνομα συμβολοσειράς). ο tg2- ο γονέας της ομάδας αναφερόμενου νήματος είναι ΕΝΑ επειδή tg1Η αναφορά περνά ως επιχείρημα στο ThreadGroup (tg1, "Β") και ΕΝΑ συνεργάζεται με tg1.

Υπόδειξη: Μόλις δεν χρειάζεστε πλέον μια ιεραρχία ThreadGroup αντικείμενα, καλέστε ThreadGroup'μικρό άκυρη καταστροφή () μέθοδο μέσω αναφοράς στο ThreadGroup αντικείμενο στην κορυφή αυτής της ιεραρχίας. Εάν η κορυφή ThreadGroup αντικείμενο και όλα τα αντικείμενα υποομάδας στερούνται αντικειμένων νήματος, καταστρέφω() προετοιμάζει αυτά τα αντικείμενα της ομάδας νημάτων για συλλογή απορριμμάτων. Σε διαφορετική περίπτωση, καταστρέφω() ρίχνει ένα IllegalThreadStateException αντικείμενο. Ωστόσο, έως ότου ακυρώσετε την αναφορά στην κορυφή ThreadGroup αντικείμενο (υποθέτοντας ότι μια μεταβλητή πεδίου περιέχει αυτήν την αναφορά), ο συλλέκτης απορριμμάτων δεν μπορεί να συλλέξει αυτό το αντικείμενο. Αναφερόμενοι στο πάνω αντικείμενο, μπορείτε να προσδιορίσετε εάν πραγματοποιήθηκε προηγούμενη κλήση στο καταστρέφω() μέθοδο καλώντας ThreadGroup'μικρό boolean isDestroyed () μέθοδος. Αυτή η μέθοδος επιστρέφει αληθινή εάν η ιεραρχία της ομάδας νήματος καταστράφηκε.

Από μόνες τους, οι ομάδες νήματος είναι άχρηστες. Για να είναι οποιαδήποτε χρήση, πρέπει να ομαδοποιούν νήματα. Ομαδοποιείτε τα νήματα σε ομάδες νημάτων περνώντας ThreadGroup αναφορές σε κατάλληλες Νήμα κατασκευαστές:

ThreadGroup tg = νέο ThreadGroup ("υποομάδα 2"); Νήμα t = νέο νήμα (tg, "το νήμα μου");

Ο παραπάνω κωδικός δημιουργεί πρώτα ένα υποομάδα 2 ομάδα με κύριος ως γονική ομάδα. (Υποθέτω ότι το κύριο νήμα εκτελεί τον κώδικα.) Ο κώδικας στη συνέχεια δημιουργεί ένα το νήμα μουΝήμα αντικείμενο στο υποομάδα 2 ομάδα.

Τώρα, ας δημιουργήσουμε μια εφαρμογή που παράγει την ιεραρχική δομή της ομάδας νήματος της φιγούρας μας:

Λίστα 1. ThreadGroupDemo.java

// ThreadGroupDemo.java class ThreadGroupDemo {public static void main (String [] args) {ThreadGroup tg = new ThreadGroup ("υποομάδα 1"); Νήμα t1 = νέο νήμα (tg, "νήμα 1"); Νήμα t2 = νέο νήμα (tg, "νήμα 2"); Νήμα t3 = νέο νήμα (tg, "νήμα 3"); tg = νέο ThreadGroup ("υποομάδα 2"); Νήμα t4 = νέο νήμα (tg, "το νήμα μου"); tg = Thread.currentThread () .getThreadGroup (); int agc = tg.activeGroupCount (); System.out.println ("Ενεργές ομάδες νημάτων σε" + tg.getName () + "ομάδα νημάτων:" + agc); tg.list (); }}

ThreadGroupDemo δημιουργεί την κατάλληλη ομάδα νήματος και αντικείμενα νήματος για να αντικατοπτρίζει αυτό που βλέπετε στην παραπάνω εικόνα. Για να αποδείξει ότι το υποομάδα 1 και υποομάδα 2 ομάδες είναι κύριοςμόνο οι υποομάδες, ThreadGroupDemo κάνει τα εξής:

  1. Ανακτά μια αναφορά στα κύρια νήματα ThreadGroup αντικείμενο καλώντας Νήμαστατικό currentThread () μέθοδος (η οποία επιστρέφει μια αναφορά στα κύρια νήματα Νήμα αντικείμενο) ακολουθούμενο από Νήμα'μικρό ThreadGroup getThreadGroup () μέθοδος.
  2. Κλήσεις ThreadGroup'μικρό int activeGroupCount () μέθοδο για το μόλις επέστρεψε ThreadGroup αναφορά για επιστροφή μιας εκτίμησης ενεργών ομάδων στην ομάδα νήματος του κύριου νήματος.
  3. Κλήσεις ThreadGroup'μικρό Συμβολοσειρά getName () μέθοδος για να επιστρέψετε το όνομα της ομάδας νήματος του κύριου νήματος.
  4. Κλήσεις ThreadGroup'μικρό άκυρη λίστα () μέθοδος εκτύπωσης στις τυπικές λεπτομέρειες της συσκευής εξόδου στην ομάδα νημάτων του βασικού νήματος και σε όλες τις υποομάδες.

Όταν τρέχετε, ThreadGroupDemo εμφανίζει την ακόλουθη έξοδο:

Ενεργές ομάδες νήματος στην κύρια ομάδα νήματος: 2 java.lang.ThreadGroup [name = main, maxpri = 10] Thread [main, 5, main] Thread [Thread-0,5, main] java.lang.ThreadGroup [name = υποομάδα 1, maxpri = 10] Νήμα [νήμα 1,5, υποομάδα 1] Νήμα [νήμα 2,5, υποομάδα 1] Νήμα [νήμα 3,5, υποομάδα 1] java.lang.ThreadGroup [όνομα = υποομάδα 2, maxpri = 10 ] Νήμα [το νήμα μου, 5, υποομάδα 2]

Έξοδος που ξεκινά με Νήμα αποτελέσματα από λίστα()εσωτερικές κλήσεις προς Νήμα'μικρό toString () μέθοδος, μια μορφή εξόδου που περιέγραψα στο Μέρος 1. Μαζί με αυτήν την έξοδο, βλέπετε την έξοδο που ξεκινά με java.lang.ThreadGroup. Αυτή η έξοδος προσδιορίζει το όνομα της ομάδας νήματος ακολουθούμενη από τη μέγιστη προτεραιότητά της.

Ομάδες προτεραιότητας και νήματος

Η μέγιστη προτεραιότητα μιας ομάδας νήματος είναι η υψηλότερη προτεραιότητα που μπορεί να επιτύχει οποιοδήποτε νήμα. Εξετάστε το προαναφερθέν πρόγραμμα διακομιστή δικτύου. Μέσα σε αυτό το πρόγραμμα, ένα νήμα περιμένει και δέχεται αιτήματα από προγράμματα πελατών. Πριν το κάνετε αυτό, το νήμα αναμονής / αποδοχής-αιτήματος μπορεί πρώτα να δημιουργήσει μια ομάδα νημάτων με μέγιστη προτεραιότητα ακριβώς κάτω από την προτεραιότητα αυτού του νήματος. Αργότερα, όταν φτάσει ένα αίτημα, το νήμα αναμονής / αποδοχής-αιτήματος δημιουργεί ένα νέο νήμα για να απαντήσει στο αίτημα του πελάτη και προσθέτει το νέο νήμα στην ομάδα νήματος που δημιουργήθηκε προηγουμένως Η προτεραιότητα του νέου νήματος μειώνεται αυτόματα στο μέγιστο της ομάδας νήματος. Με αυτόν τον τρόπο, το νήμα αναμονής / αποδοχής-αιτήματος ανταποκρίνεται συχνότερα στα αιτήματα επειδή εκτελείται συχνότερα.

Η Java εκχωρεί μέγιστη προτεραιότητα σε κάθε ομάδα νήματος. Όταν δημιουργείτε μια ομάδα, η Java αποκτά αυτήν την προτεραιότητα από τη μητρική της ομάδα. Χρήση ThreadGroup'μικρό void setMaxPriority (int προτεραιότητα) στη συνέχεια να ορίσετε τη μέγιστη προτεραιότητα. Τυχόν νήματα που προσθέτετε στην ομάδα μετά τον ορισμό της μέγιστης προτεραιότητάς του δεν μπορούν να έχουν προτεραιότητα που υπερβαίνει το μέγιστο. Κάθε νήμα με υψηλότερη προτεραιότητα χαμηλώνει αυτόματα όταν συμμετέχει στην ομάδα νήματος. Ωστόσο, εάν χρησιμοποιείτε setMaxPriority (int προτεραιότητα) για να μειώσετε τη μέγιστη προτεραιότητα μιας ομάδας, όλα τα νήματα που προστέθηκαν στην ομάδα πριν από αυτήν τη μέθοδο κλήσης διατηρούν τις αρχικές τους προτεραιότητες. Για παράδειγμα, εάν προσθέσετε ένα νήμα προτεραιότητας 8 σε μια ομάδα μέγιστης προτεραιότητας 9 και στη συνέχεια μειώσετε τη μέγιστη προτεραιότητα αυτής της ομάδας σε 7, το νήμα προτεραιότητας 8 παραμένει στην προτεραιότητα 8. Ανά πάσα στιγμή, μπορείτε να καθορίσετε τη μέγιστη προτεραιότητα μιας ομάδας νήματος καλώντας ThreadGroup'μικρό int getMaxPriority () μέθοδος. Για να δείξω ομάδες προτεραιότητας και νήμα, έγραψα MaxPriorityDemo:

Λίστα 2. MaxPriorityDemo.java

// MaxPriorityDemo.java class MaxPriorityDemo {public static void main (String [] args) {ThreadGroup tg = new ThreadGroup ("A"); System.out.println ("tg μέγιστη προτεραιότητα =" + tg.getMaxPriority ()); Νήμα t1 = νέο νήμα (tg, "X"); System.out.println ("t1 priority =" + t1.getPriority ()); t1.setPriority (Νήμα.NORM_PRIORITY + 1); System.out.println ("προτεραιότητα t1 μετά το setPriority () =" + t1.getPriority ()); tg.setMaxPriority (Νήμα.NORM_PRIORITY - 1); System.out.println ("tg μέγιστη προτεραιότητα μετά το setMaxPriority () =" + tg.getMaxPriority ()); System.out.println ("προτεραιότητα t1 μετά το setMaxPriority () =" + t1.getPriority ()); Νήμα t2 = νέο νήμα (tg, "Y"); System.out.println ("t2 priority =" + t2.getPriority ()); t2.setPriority (Νήμα.NORM_PRIORITY); System.out.println ("προτεραιότητα t2 μετά το setPriority () =" + t2.getPriority ()); }}

Όταν τρέχετε, MaxPriorityDemo παράγει την ακόλουθη έξοδο:

μέγιστη προτεραιότητα tg = προτεραιότητα 10 t1 = προτεραιότητα 5 t1 μετά το setPriority () = μέγιστη προτεραιότητα 6 tg μετά το setMaxPriority () = 4 t1 προτεραιότητα μετά το setMaxPriority () = 6 t2 προτεραιότητα = 4 t2 προτεραιότητα μετά το setPriority () = 4

Ομάδα νημάτων ΕΝΑ (οι οποίες tg αναφορές) ξεκινά με την υψηλότερη προτεραιότητα (10) ως τη μέγιστη. Νήμα Χ, του οποίου Νήμα αντικείμενο t1 αναφορές, συμμετέχει στην ομάδα και λαμβάνει 5 ως προτεραιότητα. Αλλάζουμε την προτεραιότητα αυτού του νήματος σε 6, η οποία πετυχαίνει επειδή το 6 είναι μικρότερο από 10. Στη συνέχεια, καλούμε setMaxPriority (int προτεραιότητα) για να μειώσετε τη μέγιστη προτεραιότητα της ομάδας σε 4. Παρά το νήμα Χ παραμένει στην προτεραιότητα 6, προστέθηκε πρόσφατα Γ Το νήμα λαμβάνει 4 ως προτεραιότητά του. Τέλος, μια προσπάθεια αύξησης του νήματος ΓΗ προτεραιότητα σε 5 αποτυγχάνει, επειδή το 5 είναι μεγαλύτερο από το 4.

Σημείωση:setMaxPriority (int προτεραιότητα) προσαρμόζει αυτόματα τη μέγιστη προτεραιότητα των υποομάδων μιας ομάδας νήματος.

Εκτός από τη χρήση ομάδων νημάτων για τον περιορισμό της προτεραιότητας νήματος, μπορείτε να ολοκληρώσετε άλλες εργασίες καλώντας διάφορες ThreadGroup μεθόδους που ισχύουν για το νήμα κάθε ομάδας. Οι μέθοδοι περιλαμβάνουν άκυρη αναστολή (), άκυρο βιογραφικό (), άκυρη στάση (), και άκυρη διακοπή (). Επειδή η Sun Microsystems έχει καταργήσει τις τρεις πρώτες μεθόδους (δεν είναι ασφαλείς), εξετάζουμε μόνο διακοπή().

Διακοπή μιας ομάδας νήματος

ThreadGroup'μικρό διακοπή() Η μέθοδος επιτρέπει σε ένα νήμα να διακόψει τα νήματα και τις υποομάδες μιας συγκεκριμένης ομάδας νήματος. Αυτή η τεχνική θα αποδειχθεί κατάλληλη στο ακόλουθο σενάριο: Το κύριο νήμα της εφαρμογής σας δημιουργεί πολλά νήματα που το καθένα εκτελεί μια μονάδα εργασίας. Επειδή όλα τα νήματα πρέπει να ολοκληρώσουν τις αντίστοιχες μονάδες εργασίας τους πριν από οποιοδήποτε νήμα μπορεί να εξετάσει τα αποτελέσματα, κάθε νήμα περιμένει μετά την ολοκλήρωση της μονάδας εργασίας του. Το κύριο νήμα παρακολουθεί την κατάσταση εργασίας. Μόλις περιμένουν όλα τα άλλα νήματα, το κύριο νήμα καλεί διακοπή() για να διακόψετε τις αναμονές των άλλων νημάτων. Στη συνέχεια, αυτά τα θέματα μπορούν να εξετάσουν και να επεξεργαστούν τα αποτελέσματα. Η καταχώριση 3 δείχνει διακοπή της ομάδας νημάτων:

Λίστα 3. InterruptThreadGroup.java

// InterruptThreadGroup.java class InterruptThreadGroup {public static void main (String [] args) {MyThread mt = νέο MyThread (); mt.setName ("A"); mtstart (); mt = νέο MyThread (); mt.setName ("B"); mtstart (); δοκιμάστε το {Thread.sleep (2000); // Περιμένετε 2 δευτερόλεπτα} catch (InterruptException e) {} // Διακοπή όλων των μεθόδων στην ίδια ομάδα νήματος με το κύριο // thread Thread.currentThread () .getThreadGroup () .interrupt (); }} Η κλάση MyThread επεκτείνει το νήμα {public void run () {synchronized ("A") {System.out.println (getName () + "about to waiting.") δοκιμάστε το "A" .wait (); } catch (InterruptException e) {System.out.println (getName () + "διακοπή."); } System.out.println (getName () + "τερματισμός."); }}}