This shows you the differences between two versions of the page.
|
java:thread_signalling [2015/03/29 20:16] gthanos [Συγχρονισμός με χρήση wait(), notify(), notifyAll()] |
java:thread_signalling [2016/02/26 11:15] |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Συγχρονισμός Νημάτων ====== | ||
| - | |||
| - | Ο συγχρονισμός νημάτων έχει σαν στόχο να επιτρέπει σε διαφορετικά νήματα να διαβάζουν ή να γράφουν με ασφάλεια σε διαμοιραζόμενες μεταβλητές χωρίς να προκύπτουν ασάφειες ως προς τις τιμές των μεταβλητών αυτών λόγω ταυτόχρονης μεταβολής τους. Ας υποθέσουμε ότι έχουμε δύο νήματα Α, Β και το νήμα Α θέλει να ειδοποιήσει το νήμα Β μόλις ολοκληρώσει την επεξεργασία των δεδομένων του, ώστε εκείνο να ξεκινήσει την επεξεργασία των δεδομένων. | ||
| - | |||
| - | ===== Συγχρονισμός μέσω διαμοιραζόμενων αντικειμένων ===== | ||
| - | |||
| - | Ο πιο απλός τρόπος για να επικοινωνήσουν δύο νήματα μεταξύ τους είναι μέσω διαμοιραζόμενων αντικειμένων. Για το παραπάνω παράδειγμα, ας υποθέσουμε ότι το νήμα Α θέτει την boolean τιμή //hasDataToProcess// σε true μέσα από μία συγχρονισμένη μέθοδο και το νήμα Β διαβάζει την τιμή //hasDataToProcess// και πάλι μέσω μίας συγχρονισμένης μεθόδου | ||
| - | |||
| - | <code java MySignal.java> | ||
| - | public class MySignal{ | ||
| - | |||
| - | protected boolean hasDataToProcess = false; | ||
| - | |||
| - | public synchronized boolean hasDataToProcess(){ | ||
| - | return this.hasDataToProcess; | ||
| - | } | ||
| - | |||
| - | public synchronized void setHasDataToProcess(boolean hasData){ | ||
| - | this.hasDataToProcess = hasData; | ||
| - | } | ||
| - | |||
| - | } | ||
| - | </code> | ||
| - | |||
| - | ===== Συγχρονισμός μέσω διαρκούς επανάληψης ===== | ||
| - | |||
| - | Το νήμα Β περιμένει έως ότου τα δεδομένα να είναι διαθέσιμα περιμένοντας διαρκώς σε ένα while() βρόγχο (loop), όπως παρακάτω | ||
| - | |||
| - | <code java> | ||
| - | protected MySignal sharedSignal = ... | ||
| - | |||
| - | ... | ||
| - | |||
| - | while(!sharedSignal.hasDataToProcess()){ | ||
| - | //do nothing... busy waiting | ||
| - | } | ||
| - | </code> | ||
| - | Στο παράδειγμα αυτό το νήμα περιμένει, αλλά παράλληλα παραμένει και ενεργό (busy). | ||
| - | |||
| - | ===== Συγχρονισμός με χρήση 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()]]. | ||
| - | |||
| - | Όταν ένα νήμα καλεί την μέθοδο //wait()// πάνω σε ένα αντικείμενο, τότε το νήμα κοιμάται έως ότου ένα άλλο νήμα καλέσει τη μέθοδο //notify()// πάνω στο ίδιο αντικείμενο. Τότε το νήμα που κοιμόταν ξυπνάει και συνεχίζει την εκτέλεση του. Προκειμένου να κληθούν οι //wait()// και //notify()// __το νήμα που τις καλεί θα πρέπει να λάβει το monitor lock για το συγκεκριμένο αντικείμενο__, δηλαδή τόσο η //wait()// όσο και η //notify()// θα πρέπει να κληθούν μέσα σε μία συγχρονισμένη μέθοδο ή συγχρονισμένο μπλοκ. Δείτε το παρακάτω παράδειγμα συγχρονισμού με χρήση των //wait()/notify()//. | ||
| - | |||
| - | <code java MonitorObject.java> | ||
| - | public class MonitorObject{ | ||
| - | } | ||
| - | </code> | ||
| - | |||
| - | <code java MyWaitNotify.java> | ||
| - | public class MyWaitNotify{ | ||
| - | |||
| - | MonitorObject myMonitorObject = new MonitorObject(); | ||
| - | |||
| - | public void doWait(){ | ||
| - | synchronized(myMonitorObject){ | ||
| - | try{ | ||
| - | myMonitorObject.wait(); | ||
| - | } catch(InterruptedException e){...} | ||
| - | } | ||
| - | } | ||
| - | |||
| - | public void doNotify(){ | ||
| - | synchronized(myMonitorObject){ | ||
| - | myMonitorObject.notify(); | ||
| - | } | ||
| - | } | ||
| - | } | ||
| - | </code> | ||
| - | |||
| - | Η μέθοδος //notify()// ξυπνάει ένα νήμα που περιμένει στο συγκεκριμένο //monitor lock//. Όπως βλέπετε οι μέθοδοι wait() και notify() καλούνται μέσα σε ένα συγχρονισμένο block του οποίου το monitor lock αφορά το αντικείμενο με το οποίο καλούνται οι //wait()/notify()//. Το παραπάνω σχήμα είναι υποχρεωτικό, δηλαδή δεν μπορούμε να αποκτήσουμε το monitor lock ενός διαφορετικού αντικειμένου από αυτό με το οποίο καλούμε τις wait()/notify(). | ||
| - | |||
| - | Εκτός της //notify()// υπάρχει και η //notifyAll()// που ξυπνάει όλα τα νήματα που περιμένουν στο συγκεκριμένο //monitor lock//. | ||
| - | |||
| - | |||