java:thread_signalling

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
java:thread_signalling [2015/03/30 04:10]
gthanos [Spurious wake-ups]
java:thread_signalling [2016/02/26 11:15] (current)
Line 1: Line 1:
 ====== Συγχρονισμός Νημάτων ====== ====== Συγχρονισμός Νημάτων ======
  
-Ο συγχρονισμός νημάτων έχει σαν στόχο να επιτρέπει σε διαφορετικά νήματα να διαβάζουν ή να γράφουν με ασφάλεια σε διαμοιραζόμενες μεταβλητές χωρίς να προκύπτουν ασάφειες ως προς τις τιμές των μεταβλητών αυτών λόγω ταυτόχρονης μεταβολής τους. ​Ας υποθέσουμε ​ότι έχουμε δύο νήματα ​Α, Β και το νήμα ​Α θέλει να ειδοποιήσει το νήμα Β μόλις ​ολοκληρώσει την επεξεργασία των δεδομένων του, ώστε εκείνο ​να ξεκινήσει την ​επεξεργασία των δεδομένων.+Ο συγχρονισμός νημάτων έχει σαν στόχο να επιτρέπει σε διαφορετικά νήματα να διαβάζουν ή να γράφουν με ασφάλεια σε διαμοιραζόμενες μεταβλητές χωρίς να προκύπτουν ασάφειες ως προς τις τιμές των μεταβλητών αυτών λόγω ταυτόχρονης μεταβολής τους. ​Ο προσεκτικός αναγνώστης ​θα παρατηρήσει ότι το παραπάνω μπορούμε να το επιτύχουμε ​και με την χρήση συγχρονισμένων μπλοκ ή συγχρονισμένων ​μεθόδων. Το παραπάνω είναι εν μέρη σωστό, μόνο αν τα νήματα ​δύναται να εκτελούνται παράλληλα. Εάν ​τα νήματα δεν μπορούν να εκτελεστούν παράλληλα,​ αλλά το ένα ​νήμα θα πρέπει να περιμένει το άλλο να ολοκληρώσει τότε τα συγχρονισμένα block από μόνα τους δεν αποτελούν λύση. Σε αυτή ​την περίπτωση απαιτείται η έννοια της κλειδαριάς (lock)
  
-===== Συγχρονισμός μέσω διαμοιραζόμενων αντικειμένων =====+Ας υποθέσουμε ότι έχουμε δύο νήματα Α, Β και το νήμα Α θέλει να ειδοποιήσει το νήμα Β μόλις ολοκληρώσει την επεξεργασία των δεδομένων του, ώστε εκείνο να ξεκινήσει την επεξεργασία των δεδομένων που δημιουργήθηκαν. Δεν μας αρκεί δηλαδή τα νήματα Α, Β να εκτελούνται παράλληλα,​ αλλά θα θέλαμε το Β να ξεκινήσει την επεξεργασία μόλις το Α ολοκληρώσει. 
 + 
 +===== Συγχρονισμός μέσω διαμοιραζόμενων αντικειμένων ​και διαρκούς επανάληψης ​=====
  
 Ο πιο απλός τρόπος για να επικοινωνήσουν δύο νήματα μεταξύ τους είναι μέσω διαμοιραζόμενων αντικειμένων. Για το παραπάνω παράδειγμα,​ ας υποθέσουμε ότι το νήμα Α θέτει την boolean τιμή //​hasDataToProcess//​ σε true μέσα από μία συγχρονισμένη μέθοδο και το νήμα Β διαβάζει την τιμή //​hasDataToProcess//​ και πάλι μέσω μίας συγχρονισμένης μεθόδου Ο πιο απλός τρόπος για να επικοινωνήσουν δύο νήματα μεταξύ τους είναι μέσω διαμοιραζόμενων αντικειμένων. Για το παραπάνω παράδειγμα,​ ας υποθέσουμε ότι το νήμα Α θέτει την boolean τιμή //​hasDataToProcess//​ σε true μέσα από μία συγχρονισμένη μέθοδο και το νήμα Β διαβάζει την τιμή //​hasDataToProcess//​ και πάλι μέσω μίας συγχρονισμένης μεθόδου
Line 10: Line 12:
 public class MySignal{ public class MySignal{
  
-  protected boolean ​hasDataToProcess ​false;+  protected boolean ​dataToProcess ​true;
  
   public synchronized boolean hasDataToProcess(){   public synchronized boolean hasDataToProcess(){
-    return ​this.hasDataToProcess;+    return ​dataToProcess;
   }   }
  
   public synchronized void setHasDataToProcess(boolean hasData){   public synchronized void setHasDataToProcess(boolean hasData){
-    this.hasDataToProcess ​= hasData;  ​+    this.dataToProcess ​= hasData;  ​
   }   }
- 
 } }
 </​code>​ </​code>​
  
-===== Συγχρονισμός μέσω διαρκούς επανάληψης =====+Η παραπάνω κλάση μπορεί να χρησιμοποιηθεί για την επικοινωνία μεταξύ δύο νημάτων **Α**, **Β**. Ας υποθέσουμε ότι το νήμα **Β** περιμένει έως ότου τα δεδομένα να είναι διαθέσιμα από το νήμα **Α**, περιμένοντας ​διαρκώς σε ένα while() βρόγχο,​ όπως ​παρακάτω:
  
-Το νήμα Β περιμένει έως ότου τα δεδομένα να είναι διαθέσιμα περιμένοντας διαρκώς σε ένα while() βρόγχο (loop), όπως παρακάτω+<code java BusyWait.java>​ 
 +import java.util.*;​
  
-<code java> +public class BusyWait implements Runnable {
-protected MySignal sharedSignal = ...+
  
-...+  protected MySignal signal; 
 +   
 +  BusyWait(MySignal s) { 
 +    signal = s; 
 +  } 
 +   
 +  public static void main(String args[]) { 
 +    MySignal sharedSignal = new MySignal();​ 
 +    new Thread(new BusyWait(sharedSignal)).start(); 
 +    new Thread(new BusyWait(sharedSignal)).start(); 
 +  } 
 +   
 +  public void run() { 
 +    try { 
 +      while(signal.hasDataToProcess() == false) { 
 +      } 
 +      signal.setHasDataToProcess(false); ​   
 +      int i; 
 +      for(i=0; i<10; i++) { 
 +        Thread.sleep(100);​ 
 +        System.out.println(Thread.currentThread().getName()+"​ iteration: "​+i);​ 
 +      } 
 +      signal.setHasDataToProcess(true);​ 
 +      System.out.println("​Exiting!"​);​ 
 +    } 
 +    catch(InterruptedException ex) { 
 +      ex.printStackTrace();​ 
 +    } 
 +  }
  
-while(!sharedSignal.hasDataToProcess()){ 
-  //do nothing... busy waiting 
 } }
 </​code>​ </​code>​
-Στο παράδειγμα αυτό το νήμα περιμένει,​ αλλά παράλληλα παραμένει και ενεργό (busy). ​+Στο παράδειγμα αυτό το νήμα περιμένει,​ αλλά παράλληλα παραμένει και ενεργό (busy). Επίσης,​ θα παρατηρήσετε ότι με σημαντική πιθανότητα (αλλά όχι σίγουρα) τα δύο νήματα θα εκκινήσουν ταυτόχρονα και ταυτόχρονα διαβάσουν την τιμή **signal.hasDataToProcess() -> -1**  και θα αρχίσουν να εκτελούνται παράλληλα πράγμα που δεν το θέλουμε.
  
 ===== Συγχρονισμός με χρήση wait(), notify(), notifyAll() ===== ===== Συγχρονισμός με χρήση wait(), notify(), notifyAll() =====
  
-Το παραπάνω σχήμα ​αν και ​είναι αποτελεσματικό ως προς τον συγχρονισμό δαπανά πολλά resources καθώς το νήμα Β παραμένει ενεργό περιμένοντας. Θα ήταν πιο αποδοτικό αν αντί να παραμένει ενεργό το νήμα Β //"​κοιμόταν"//​ περιμένοντας το νήμα Α να ολοκληρώσει. Η Java διαθέτει ένα μηχανισμό προκειμένου να επιτρέψει σε νήματα να κοιμηθούν περιμένοντας τα νήματα που εκτελούνται να ολοκληρώσουν το έργο τους. Συγκεκριμένα διαθέτει τρεις μεθόδους που επιτρέπουν το συγκεκριμένο συγχρονισμό [[https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​lang/​Object.html#​wait--|wait()]],​ [[https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​lang/​Object.html#​notify--|notify()]],​ [[https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​lang/​Object.html#​notifyAll--|notifyAll()]].+Το παραπάνω σχήμα ​δεν είναι αποτελεσματικό ως προς τον συγχρονισμό ​και επίσης ​δαπανά πολλά resources καθώς το νήμα ​**Β** παραμένει ενεργό περιμένοντας. Θα ήταν πιο αποδοτικό αν αντί να παραμένει ενεργό το νήμα ​**Β** //"​κοιμόταν"//​ περιμένοντας το νήμα ​**Α** να ολοκληρώσει. Η Java διαθέτει ένα μηχανισμό προκειμένου να επιτρέψει σε νήματα να κοιμηθούν περιμένοντας τα νήματα που εκτελούνται να ολοκληρώσουν το έργο τους. Συγκεκριμένα διαθέτει τρεις μεθόδους που επιτρέπουν το συγκεκριμένο συγχρονισμό [[https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​lang/​Object.html#​wait--|wait()]],​ [[https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​lang/​Object.html#​notify--|notify()]],​ [[https://​docs.oracle.com/​javase/​8/​docs/​api/​java/​lang/​Object.html#​notifyAll--|notifyAll()]].
  
-Όταν ένα νήμα καλεί την μέθοδο //wait()// πάνω σε ένα αντικείμενο,​ τότε το νήμα κοιμάται έως ότου ένα άλλο νήμα καλέσει τη μέθοδο //​notify()//​ πάνω στο ίδιο αντικείμενο. Τότε το νήμα που κοιμόταν ξυπνάει και συνεχίζει την εκτέλεση του. ​Προκειμένου να κληθούν οι //wait()// και //​notify()//​ __το νήμα που τις καλεί ​θα πρέπει να λάβει το monitor lock για το συγκεκριμένο αντικείμενο__,​ δηλαδή τόσο η //wait()// όσο και η //​notify()//​ __θα πρέπει να κληθούν μέσα σε μία συγχρονισμένη μέθοδο ή συγχρονισμένο μπλοκ__. Δείτε το παρακάτω παράδειγμα συγχρονισμού με χρήση των //​wait()/​notify()//​.+Όταν ένα νήμα καλεί την μέθοδο //wait()// πάνω σε ένα αντικείμενο,​ τότε το νήμα κοιμάται έως ότου ένα άλλο νήμα καλέσει τη μέθοδο //​notify()//​ πάνω στο ίδιο αντικείμενο. Τότε το νήμα που κοιμόταν ξυπνάει και συνεχίζει την εκτέλεση του. ​Το νήμα που καλεί τις //wait()// και //​notify()//​ __θα πρέπει να λάβει το monitor lock για το συγκεκριμένο αντικείμενο__,​ δηλαδή τόσο η //wait()// όσο και η //​notify()//​ __θα πρέπει να κληθούν μέσα σε μία συγχρονισμένη μέθοδο ή συγχρονισμένο μπλοκ__. Δείτε το παρακάτω παράδειγμα συγχρονισμού με χρήση των //​wait()/​notify()//​.
  
 <code java MonitorObject.java>​ <code java MonitorObject.java>​
Line 53: Line 80:
  
   MonitorObject myMonitorObject = new MonitorObject();​   MonitorObject myMonitorObject = new MonitorObject();​
 +  boolean wasSignalled = false;
  
   public void doWait(){   public void doWait(){
     synchronized(myMonitorObject){     synchronized(myMonitorObject){
-      ​try+      ​if(!wasSignalled)
-        myMonitorObject.wait();​ +        ​try{ 
-      } catch(InterruptedException e){...}+          ​myMonitorObject.wait();​ 
 +         ​} catch(InterruptedException e){...} 
 +      } 
 +      //clear signal and continue running. 
 +      wasSignalled = false;
     }     }
   }   }
Line 64: Line 96:
   public void doNotify(){   public void doNotify(){
     synchronized(myMonitorObject){     synchronized(myMonitorObject){
 +      wasSignalled = true;
       myMonitorObject.notify();​       myMonitorObject.notify();​
     }     }
Line 70: Line 103:
 </​code>​ </​code>​
  
-Η μέθοδος //​notify()//​ ξυπνάει ένα νήμα που περιμένει στο συγκεκριμένο //monitor lock//. Όπως βλέπετε οι μέθοδοι //wait()// και //​notify()//​ καλούνται μέσα σε ένα συγχρονισμένο block του οποίου το //monitor lock// αφορά το αντικείμενο από το οποίο καλούνται οι //wait()// και //​notify()//​. Το παραπάνω σχήμα είναι υποχρεωτικό,​ δηλαδή ​+Η μέθοδος //​notify()//​ ξυπνάει ένα νήμα που περιμένει στο συγκεκριμένο //monitor lock//. Όπως βλέπετε οι μέθοδοι //wait()// και //​notify()//​ καλούνται μέσα σε ένα συγχρονισμένο block του οποίου το //monitor lock// αφορά το αντικείμενο από το οποίο καλούνται οι //wait()// και //​notify()//​. Το παραπάνω σχήμα είναι υποχρεωτικό,​ δηλαδή ​(ισχύουν και τα δύο παρακάτω):​
   - οι //wait()// και //​notify()//​ θα πρέπει να κληθούν μέσα σε ένα συγχρονισμένο block ή μία συγχρονισμένη μέθοδο. Αν δεν γίνει αυτό τότε λαμβάνουμε ένα [[http://​docs.oracle.com/​javase/​7/​docs/​api/​java/​lang/​IllegalMonitorStateException.html|IllegalMonitorStateException]].   - οι //wait()// και //​notify()//​ θα πρέπει να κληθούν μέσα σε ένα συγχρονισμένο block ή μία συγχρονισμένη μέθοδο. Αν δεν γίνει αυτό τότε λαμβάνουμε ένα [[http://​docs.oracle.com/​javase/​7/​docs/​api/​java/​lang/​IllegalMonitorStateException.html|IllegalMonitorStateException]].
-  - δεν μπορούμε να αποκτήσουμε το monitor lock ενός διαφορετικού αντικειμένου από αυτό με το οποίο καλούμε τις wait()/​notify(). Επίσης, βλέπετε ότι ​οι //wait()// και //​notify()//​ βρίσκονται μέσα σε ένα block. ​+  - Κατά την κλήση των //​wait()/​notify()// ​δεν μπορούμε να αποκτήσουμε το monitor lock ενός διαφορετικού αντικειμένου από αυτό με το οποίο καλούμε τις wait()/​notify(), δηλ δεν μπορούμε ​να κάνουμε κάτι ​σαν ​το παρακάτω
  
-Επίσης,​ εκτός της //notify()// υπάρχει και η //notifyAll()// που ξυπνάει όλα τα νήματα που περιμένουν στο συγκεκριμένο //monitor lock// σε αντίθεση με την notify() που ξυπνάει μόνο ένα νήμαΌταν περισσότερα του ενός νήματα περιμένουν το ποιο νήμα θα ξυπνήσει η //notify()// δεν ελέγχεται από εσάς.+<code java> 
 +  public void doWait()
 +    synchronized(myMonitorObject)
 +      if(!wasSignalled)
 +        try{ 
 +          myMonitorObject2.wait()
 +         } catch(InterruptedException e){...} 
 +      } 
 +      ​//clear signal and continue running. 
 +      wasSignalled = false; 
 +    } 
 +  } 
 +</​code>​
  
-Πως ​όμως το παραπάνω σχήμα ​είναι δυνατόν; Το thread ​που περιμένει ​δεν κρατά το monitor lock που έχει λάβει για το αντικείμενο ​myMonitorObject,​ όσο αυτό παραμένει μέσα στο synchronized block; Η απάντηση ​είναι αρνητική. Όσο το νήμα περιμένει ελευθερώνει το συγκεκριμένο monitor lock. Το παραπάνω επιτρέπει σε άλλες μεθόδους που ​δεν έχουν ​το lock να λάβουν ​το lock μπαίνοντας σε ένα συγχρονισμένο block ή συγχρονισμένη συνάρτηση και να καλέσουν τις //wait()// και //​notify()// ​μέσα από εκεί.+Εκτός της //​notify()//​ υπάρχει και η //​notifyAll()//​ που ξυπνάει όλα τα νήματα ​που περιμένουν στο συγκεκριμένο //monitor lock// σε αντίθεση με την notify() που ξυπνάει μόνο ​ένα νήμα. Όταν περισσότερα ​του ενός ​νήματα περιμένουν το ποιο νήμα θα ξυπνήσει η μέθοδος ​//​notify()// ​δεν ​ελέγχεται από εσάς. Είστε όμως σίγουροι ​ότι θα ξυπνήσει ένα νήμα.
  
-Once a thread is awakened it cannot exit the wait() call until the thread calling ​notify() ​has left its synchronized block. In other words: The awakened thread must reobtain the lock on the monitor object before it can exit the wait() call, because the wait call is nested inside a synchronized block. If multiple threads are awakened using notifyAll() only one awakened thread at a time can exit the wait() method, since each thread must obtain the lock on the monitor object in turn before exiting wait().+<WRAP important 80% round center>​ 
 +Ο παραπάνω κώδικας δεν μπορεί να εξασφαλίσει τον συγχρονισμό αν αντικαταστήσουμε την ​notify() ​με την ​notifyAll(). ​Γιατί;​ 
 +</​WRAP>​
  
-Όταν ένα νήμα ​ξυπνήσει δεν ​μπορεί αμέσως να βγει από την μέθοδο //wait()//. Αντίθετα ​θα πρέπει να περιμένει μέχρι το νήμα που κάλεσε την //​notify()//​ να βγει από ​το συγχρονισμένο block ή την συγχρονισμένη μέθοδο.  Αυτό συμβαίνει διότι το νήμα που ξυπνάει θα πρέπει να λάβει το //lock// του αντικειμένου από το οποίο κλήθηκε η μέθοδος //wait()// καθώς ​αυτή καλείτε ​πάντα μέσα σε ένα συγχρονισμένο block ή μία συγχρονισμένη μέθοδο. Σε αναλογία,​ αν πολλαπλά ​νήματα ​ξυπνήσουν ​μέσα από μία κλήση της //​notifyAll()//,​ τότε αυτά τα νήματα δεν μπορούν ​να ξυπνήσουν όλα ​μαζί, αλλά ένα-ένα καθώς κάθε ένα ​νήμα που ξυπνάει θα πρέπει ​να λάβει το lock πάνω ​στο οποίο περιμένει πριν ​συνεχίσει ​την εκτέλεση του.+Πως όμως ​το παραπάνω σχήμα ​είναι δυνατόν; Το thread ​που περιμένει δεν ​κρατά το monitor lock που έχει λάβει για το αντικείμενο myMonitorObject,​ όσο αυτό παραμένει μέσα στο συγχρονισμένο block ή την συγχρονισμένη μέθοδο; Η απάντηση είναι αρνητική. Όσο ​το νήμα περιμένει ελευθερώνει το συγκεκριμένο ​monitor lock. Το παραπάνω επιτρέπει σε άλλα νήματα που ​δεν έχουν το lock να λάβουν το lock μπαίνοντας σε ένα ​συγχρονισμένο ​block ή συγχρονισμένη συνάρτηση. ​
  
-===== Χαμένα σήματα συγχρονισμού ​=====+<WRAP tip 80% round center>​ 
 +Στην πραγματικότητα αυτό που συμβαίνει είναι λίγο πιο πολύπλοκο από όσο περιγράφεται παραπάνω. Όταν ​ένα ​νήμα ξυπνήσει δεν μπορεί αμέσως να βγει από την μέθοδο //wait()//. Αντίθετα θα πρέπει να περιμένει μέχρι το νήμα ​που κάλεσε ​την //​notify()//​ να βγει από το συγχρονισμένο block ή την συγχρονισμένη μέθοδο. Αυτό συμβαίνει διότι το νήμα που ξυπνάει θα πρέπει να λάβει το //lock// του αντικειμένου από το οποίο κλήθηκε η μέθοδος //wait()// καθώς αυτή καλείται πάντα μέσα σε ένα συγχρονισμένο block ή μία συγχρονισμένη μέθοδο. Σε αναλογία,​ αν πολλαπλά νήματα ξυπνήσουν μέσα από μία κλήση της //​notifyAll()//,​ τότε αυτά τα νήματα δεν μπορούν να ξυπνήσουν όλα μαζί, αλλά ένα-ένα καθώς κάθε ένα νήμα που ξυπνάει θα πρέπει να λάβει το lock πάνω στο οποίο περιμένει πριν συνεχίσει την εκτέλεση του. 
 +</​WRAP>​
  
-Στο ​παραπάνω παράδειγμα η μέθοδος //doWait()// δεν ελέγχει εάν έχει έρθει ένα σήμα ή όχι ​πριν πάει για ύπνο το νήμα που την ​καλεί. Αν ένα σήμα ​έρθει πριν εκείνη κοιμηθεί ​δεν έχει τρόπο να ελέγξει αν το σήμα ​έφτασε ​ή όχι. Για τον λόγο αυτό ​χρησιμοποιούμε μία επιπλέον boolean ​μεταβλητή που ​κρατά την πληροφορία κατά πόσο δημιουργήθηκε ένα σήμα ή όχι. Ο παραπάνω κώδικας αναμορφώνεται ως εξής:+===== Ξαφνικά ξυπνήματα (Spurious wake-ups) ===== 
 + 
 +Υπάρχει η πιθανότητα ένα νήμα να ξυπνήσει ακόμη και ​δίχως να κληθεί η //notify()// ή η //​notifyAll()//​. Αν και οι περιπτώσεις αυτές δεν είναι συχνές δεν θα πρέπει ​να αποκλειστούν. Σε αυτή την ​περίπτωση αν το νήμα ​λάβει το **lock** θα ξυπνήσει και θα συνεχίσει την εκτέλεση του δίχως ​να εξετάσει αν έχει έρθει ένα ​σήμα ή όχι. Για το λόγο αυτό, καλό θα είναι ο έλεγχος ​που γίνεται παραπάνω (μέσα στην //if()//) να αντικατασταθεί με ένα while() loop ώστε να είμαστε σίγουροι ότι όσο δεν έχει έρθει ​κάποιο ​σήμα το νήμα που περιμένει δεν θα συνεχίσει την εκτέλεση τουΔείτε τον τελικό ​κώδικα ​παρακάτω
  
 <code java MyWaitNotify2.java>​ <code java MyWaitNotify2.java>​
Line 94: Line 145:
   public void doWait(){   public void doWait(){
     synchronized(myMonitorObject){     synchronized(myMonitorObject){
-      ​if(!wasSignalled){+      ​while(!wasSignalled){
         try{         try{
           myMonitorObject.wait();​           myMonitorObject.wait();​
Line 112: Line 163:
 } }
 </​code>​ </​code>​
-===== Ξαφνικά ξυπνήματα (Spurious wake-ups) ===== 
  
-Υπάρχει η πιθανότητα ένα νήμα ​να ξυπνήσε ακόμη και δίχως να κληθεί η //​notify()//​ ή η //​notifyAll()//​. Σε αυτή την περίπτωση αν το νήμα λάβει το lock θα ξυπνήσει και ​θα συνεχίσει την εκτέλεση του ​δίχως να εξετάσει αν έχει έρθει ένα ​σήμα ή όχι. Για το λόγο αυτόκαλό ​θα είναι ο έλεγχος που γίνεται ​παραπάνω (μέσα στην //if()//) να αντικατασταθεί με ένα ​while() loop ώστε να είμαστε σίγουροι ότι όσο δεν έχει έρθει κάποιο σήμα το νήμα ​που ​περιμένει δεν θα συνεχίσει την ​εκτέλεση του. Δείτε τον τελικό κώδικα παρακάτω +<WRAP info 80% center round> 
-<code java> +Το παραπάνω σχήμα δουλεύει επίσης πολύ καλά εάν πολλαπλά νήματα περιμένουν σε ένα monitor lock.  
-public class MyWaitNotify3{+Αν τα νήματα αυτά ​ξυπνούν μέσα από μία κλήση της μεθόδου ​//​notifyAll()//​, τότε μέσα από ​τον παραπάνω κώδικα μόνο ένα από τα νήματα θα λάβει το monitor ​lock (το πρώτο που ​ξύπνησε και έλαβε το lock του αντικειμένου myMonitorObject). Τα υπόλοιπα, όταν με την σειρά ​τους θα λάβουν το lock, θα ελέγξουν ​την τιμή της μεταβλητής //​wasSignalled//​ και θα πάνε πάλι για ύπνο εφόσον την ​βρουν ίση με //false//
 +</​WRAP>​ 
 + 
 +Παρατηρήστε ότι η κλάση **MyWaitNotify2** υλοποιεί ένα ​μία απλή κλειδαριά,​ όπου μόνο ένα νήμα μπορεί να πάρει την κλειδαριά, ενώ όλα τα υπόλοιπα νήματα περιμένουν. Μόλις ολοκληρώσει την επεξεργασία ελευθερώνει την κλειδαριά και ενημερώνει ένα από ​τα νήματα που περιμένουν να ξεκινήσει. Η παραπάνω ​κλάση θα μπορούσε να γραφεί ​και ως εξής: 
 + 
 +<​code ​java SimpleLock.java> 
 +public class SimpleLock ​{
  
   MonitorObject myMonitorObject = new MonitorObject();​   MonitorObject myMonitorObject = new MonitorObject();​
   boolean wasSignalled = false;   boolean wasSignalled = false;
  
-  public void doWait(){+  public void lock(){
     synchronized(myMonitorObject){     synchronized(myMonitorObject){
       while(!wasSignalled){       while(!wasSignalled){
Line 133: Line 189:
   }   }
  
-  public void doNotify(){+  public void unlock(){
     synchronized(myMonitorObject){     synchronized(myMonitorObject){
       wasSignalled = true;       wasSignalled = true;
Line 141: Line 197:
 } }
 </​code>​ </​code>​
- 
-Το παραπάνω σχήμα δουλεύει επίσης πολύ καλά εάν πολλαπλά νήματα περιμένουν σε ένα monitor lock.  
-Αν τα νήματα αυτά ξυπνούν μέσα από μία κλήση της μεθόδου //​notifyAll()//,​ τότε μέσα από τον παραπάνω κώδικα μόνο ένα από τα νήματα θα λάβει το monitor lock (το πρώτο που ξύπνησε και έλαβε το lock του αντικειμένου myMonitorObject). Τα υπόλοιπα,​ όταν με την σειρά τους θα λάβουν το lock, θα ελέγξουν την τιμή της μεταβλητής //​wasSignalled//​ και θα πάνε πάλι για ύπνο εφόσον την βρουν ίση με //false//. 
- 
- 
  
java/thread_signalling.1427688601.txt.gz · Last modified: 2016/02/26 11:15 (external edit)