Table of Contents

Συγχρονισμένες μέθοδοι και συγχρονισμένα blocks

Η Java παρέχει δύο βασικούς τρόπους συγχρονισμού προκειμένου να αποφεύγονται τα προβλήματα συγχρονισμού που παρουσιάστηκαν προηγούμενα α) τις συγχρονισμένες μεθόδους και β) τα συγχρονισμένα blocks.

Η διαδικασίες συγχρονισμού που περιγράφονται παρακάτω βασίζονται σε έναν εσωτερικό μηχανισμό που διαθέτουν όλα τα αντικείμενα στην Java, το λεγόμενο intrinsic lock ή monitor lock. (Το API της Java αναφέρεται συχνά στον μηχανισμό αυτό ως monitor). Όταν ένα νήμα θέλει να εκτελέσει τη συγχρονισμένη μέθοδο ενός αντικειμένου θα πρέπει να λάβει το monitor lock για το αντικείμενο αυτό. Αντίστοιχα, όταν το ίδιο νήμα θα βγει από την συγχρονισμένη μέθοδο του αντικειμένου θα ελευθερώσει το monitor lock για το αντικείμενο. Όταν ένα νήμα έχει το monitor lock για ένα αντικείμενο, κανένα άλλο νήμα δεν μπορεί να λάβει ταυτόχρονα αυτό το lock. Μόνο το υφιστάμενο αντικείμενο μπορεί να αφήσει το lock εξερχόμενο από την συγχρονισμένη μέθοδο ή το συγχρονισμένο block.

Συγχρονισμένες μέθοδοι

Προκειμένου να δημιουργήσετε μία συγχρονισμένη μέθοδο σε μία κλάση αρκεί να προσθέσετε το keywords synchronized κατά την δήλωση της μεθόδου, όπως παρακάτω

SynchronizedCounter.java
public class SynchronizedCounter {
    private int c = 0;
 
    public synchronized void increment() {
        c++;
    }
 
    public synchronized void decrement() {
        c--;
    }
 
    public synchronized int value() {
        return c;
    }
}

Αν έχουμε ένα αντικείμενο counter της παραπάνω κλάσης τότε ισχύουν τα παρακάτω

Συγχρονισμένα Βlocks

Σε αναλογία με τις συγχρονισμένες μεθόδους ορίζονται και τα συγχρονισμένα blocks με την διαφορά ότι τα συχρονισμένα blocks οφείλουν να ορίσουν το αντικείμενο του οποίου λαμβάνουν το monitor lock, όπως παρακάτω

public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

Στο παρακάτω παράδειγμα έχουμε δύο συγχρονισμένα blocκ μέσα σε δύο μη συγχρονισμένες μεθόδους. Η διαφορά είναι ότι η κλάση ορίζει δύο επιπλέον αντικείμενα μόνο και μόνο για να χρησιμοποιήσει τα monitor locks των αντικειμένων αυτών

SyncBlock.java
public class SyncBlock {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();
 
    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }
 
    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}

Αν και ένα νήμα δεν μπορεί να λάβει το monitor lock ενός αντικειμένου, όταν αυτό έχει καταληφθεί από άλλο αντικείμενο, το ίδιο thread μπορεί να λάβει το monitor lock του ιδίου αντικειμένου όσες φορές χρειαστεί αν υπάρχουν εμφωλευμένες (nested) κλήσεις συγχρονισμένων μεθόδων ή συγχρονισμένων blocks. Σε αυτές τις περιπτώσεις το monitor lock απελευθερώνεται μόνο όταν το νήμα βγει και από την τελευταία μέθοδο ή block στην οποία μπήκε πριν από κάθε άλλη κλήση συγχρονισμένης μεθόδου ή μπλοκ.

Δείτε το παραπάνω παράδειγμα εμφωλευμένων κλήσεων συγχρονισμένων μεθόδων

NestedSyncMethods.java
public class NestedSyncMethods {
  void synchronized outer() {
    inner();
  }
 
  void synchronized inner() {
   ...
  }
}