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//. | ||
- | |||
- | |||