Sobes.tech
Middle

Τι είναι το μπλοκ synchronized στη Java και ποια προβλήματα μπορεί να προκύψουν κατά τη χρήση του;

sobes.tech AI

Απάντηση από AI

Το μπλοκ synchronized στη Java χρησιμοποιείται για την εξασφάλιση συγχρονισμού στην πρόσβαση σε κοινό πόρο μεταξύ πολλαπλών νημάτων. Αυτό διασφαλίζει ότι μόνο ένα νήμα μπορεί να εκτελεί τον κώδικα μέσα στο μπλοκ σε οποιαδήποτε χρονική στιγμή.

Δομή του μπλοκ synchronized:

synchronized (αντικείμενο_μοναδικού) {
    // Κρίσιμη ενότητα κώδικα
    // Εδώ εκτελούνται λειτουργίες με τον κοινό πόρο
}
  • αντικείμενο_μοναδικού: Οποιοδήποτε αντικείμενο που λειτουργεί ως μονοπάτι (lock). Τα νήματα που προσπαθούν να εισέλθουν στο μπλοκ synchronized με το ίδιο αντικείμενο μονοπάτι θα μπλοκαριστούν μέχρι το τρέχον νήμα να εξέλθει από το μπλοκ.

Προβλήματα που μπορεί να προκύψουν κατά τη χρήση του synchronized:

  • Αμοιβαία αποκλεισμός (Deadlock): Δύο ή περισσότερα νήματα περιμένουν την απελευθέρωση πόρου που κατέχεται από άλλο νήμα, οδηγώντας σε ακινησία του προγράμματος. Αυτό μπορεί να συμβεί αν τα νήματα προσπαθούν να αποκτήσουν μπλοκ σε διαφορετική σειρά.
  • Μείωση απόδοσης: Ο συγχρονισμός μπλοκάρει την εκτέλεση άλλων νημάτων που περιμένουν πρόσβαση στον μονοπάτι, κάτι που μπορεί να επιβραδύνει την εργασία των παράλληλων εφαρμογών.
  • Μη εφαρμοσιμότητα σε ορισμένα σενάρια: Το synchronized παρέχει αποκλειστική πρόσβαση. Σε ορισμένες περιπτώσεις, μπορεί να απαιτείται πιο ευέλικτη διαχείριση πρόσβασης (π.χ., ανάγνωση από πολλά νήματα ταυτόχρονα, εγγραφή από ένα νήμα).
  • Πολυπλοκότητα αποσφαλμάτωσης: Τα προβλήματα συγχρονισμού μπορεί να είναι μη εμφανή και δύσκολα στην αναπαραγωγή και αποσφαλμάτωση.

Παράδειγμα αμοιβαίας αποκλεισμού:

class Resource {
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void method1(Resource other) {
        synchronized (lock1) {
            try {
                Thread.sleep(100); // Μίμηση εργασίας
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " αποκτήθηκε το lock1");
            synchronized (other.lock2) {
                System.out.println(Thread.currentThread().getName() + " αποκτήθηκε το lock2 του άλλου");
            }
        }
    }

    public void method2(Resource other) {
        synchronized (lock2) {
             try {
                Thread.sleep(100); // Μίμηση εργασίας
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " αποκτήθηκε το lock2");
            synchronized (other.lock1) {
                System.out.println(Thread.currentThread().getName() + " αποκτήθηκε το lock1 του άλλου");
            }
        }
    }
}

public class DeadlockExample {
    public static void main(String[] args) {
        Resource r1 = new Resource();
        Resource r2 = new Resource();

        new Thread(() -> r1.method1(r2), "Thread-1").start();
        new Thread(() -> r2.method2(r1), "Thread-2").start();
    }
}

Σε αυτό το παράδειγμα, το Thread-1 προσπαθεί να αποκτήσει το lock1 του r1 και στη συνέχεια το lock2 του r2. Το Thread-2 προσπαθεί να αποκτήσει το lock2 του r2 και στη συνέχεια το lock1 του r1. Αν τα αποκτήσουν ταυτόχρονα, θα προκύψει αμοιβαία αποκλεισμός.