Synchronized: τι είναι, αρχή λειτουργίας και χρήση σε Java

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-03-19 Χρόνος ανάγνωσης: 8 λεπ

Το Synchronized είναι ένας ενσωματωμένος μηχανισμός συγχρονισμού στη γλώσσα Java, που παρέχει αποκλειστική πρόσβαση σε κρίσιμες ενότητες κώδικα. Σύμφωνα με την Oracle, 2024, ο τροποποιητής synchronized εγγυάται ότι μόνο ένα νήμα μπορεί να εκτελέσει την επισημασμένη μέθοδο ή μπλοκ σε μια συγκεκριμένη χρονική στιγμή. Αυτός ο μηχανισμός βασίζεται σε οθόνες — μια θεμελιώδη έννοια των λειτουργικών συστημάτων, που διασφαλίζει τη σωστή λειτουργία πολυνηματικών εφαρμογών σε όλα τα επίπεδα πολυπλοκότητας.

Κύρια σημεία

  • Synchronized — λέξη-κλειδί Java για ασφαλή πρόσβαση σε δεδομένα.
  • Οθόνη αντικειμένου — ο εσωτερικός μηχανισμός στον οποίο βασίζεται ο συγχρονισμός.
  • Synchronized μέθοδος κλειδώνει ολόκληρη τη μέθοδο σε επίπεδο στιγμιοτύπου ή κλάσης.
  • Synchronized μπλοκ επιτρέπει τον συγχρονισμό μόνο ενός μέρους του κώδικα.
  • Deadlock — ένα από τα κύρια προβλήματα σε ένθετο συγχρονισμό.

Τι είναι το synchronized;

Synchronized είναι μια λέξη-κλειδί στη Java που εγγυάται ότι μόνο ένα νήμα κάθε φορά εκτελεί το προστατευμένο τμήμα κώδικα, αποτρέποντας τη φθορά δεδομένων κατά την παράλληλη πρόσβαση. Εμφανίστηκε στην πρώτη έκδοση της Java και παραμένει ο απλούστερος τρόπος διασφάλισης της νηματικής ασφάλειας για προγραμματιστές όλων των επιπέδων.

Ορισμός και ρόλος στη Java

Ο τροποποιητής synchronized λύνει δύο προβλήματα: αμοιβαίο αποκλεισμό (mutual exclusion) και ορατότητα αλλαγών (visibility). Όταν ένα νήμα εγκαταλείπει ένα synchronized μπλοκ, όλες οι αλλαγές είναι εγγυημένα ορατές σε άλλα νήματα που εισέρχονται στο μπλοκ που έχει συγχρονιστεί στο ίδιο αντικείμενο.

Το Synchronized μπορεί να εφαρμοστεί σε ολόκληρη τη μέθοδο ή σε ένα αυθαίρετο μπλοκ κώδικα με καθορισμό του αντικειμένου-οθόνης. Και στις δύο περιπτώσεις, η JVM εισάγει τις εντολές monitorenter και monitorexit σε επίπεδο bytecode.

Ιστορικό εμφάνισης

Σε πολυνηματικές εφαρμογές χωρίς συγχρονισμό, προκύπτει συνθήκη ανταγωνισμού (race condition) — όταν δύο νήματα τροποποιούν ταυτόχρονα τα ίδια δεδομένα, οδηγώντας σε απρόβλεπτα αποτελέσματα. Το Synchronized έγινε το πρώτο και κύριο εργαλείο της Java για την αντιμετώπιση αυτού του προβλήματος, προσφέροντας απλή δηλωτική σύνταξη προσβάσιμη σε κάθε προγραμματιστή.

Πώς λειτουργεί το synchronized;

Ο μηχανισμός synchronized βασίζεται στην έννοια της οθόνης — ένα υψηλού επιπέδου πρωτόγονο συγχρονισμού ενσωματωμένο σε κάθε αντικείμενο Java. Η οθόνη συνδέεται με το αντικείμενο κατά την πρώτη χρήση ενός synchronized μπλοκ σε αυτό.

Οθόνη αντικειμένου

Κάθε αντικείμενο στη Java έχει μια σχετιζόμενη οθόνη. Όταν ένα νήμα εισέρχεται σε ένα synchronized μπλοκ, καταλαμβάνει την οθόνη του αντικειμένου. Εάν η οθόνη είναι ήδη κατειλημμένη από άλλο νήμα, το νήμα μπλοκάρεται μέχρι την απελευθέρωση. Σε bytecode, αυτό αντιστοιχεί στο ζεύγος εντολών monitorenter και monitorexit.

Καταστάσεις κλειδώματος (biased locking)

Η JVM βελτιστοποιεί το synchronized μέσω πολλών επιπέδων: biased locking (προκατειλημμένο κλείδωμα) για πρόσβαση ενός νήματος, lightweight locking (ελαφρύ κλείδωμα) σε χαμηλό ανταγωνισμό και heavyweight locking (βαρύ κλείδωμα) σε έντονο ανταγωνισμό με συμμετοχή του OS. Αυτά τα επίπεδα αυξάνουν την απόδοση χωρίς αλλαγή κώδικα.

java
class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

Ο κανόνας happens-before

Το Synchronized καθιερώνει τη σχέση happens-before: όλες οι ενέργειες σε ένα νήμα πριν από την έξοδο από ένα synchronized μπλοκ είναι ορατές σε ένα άλλο νήμα μετά την είσοδο στο μπλοκ που έχει συγχρονιστεί στο ίδιο αντικείμενο. Αυτό εγγυάται όχι μόνο τον αμοιβαίο αποκλεισμό, αλλά και τη συνέπεια δεδομένων για όλα τα νήματα.

Synchronized μέθοδος vs μπλοκ

Η Java προσφέρει δύο τρόπους εφαρμογής του synchronized: σε επίπεδο μεθόδου και σε επίπεδο μπλοκ. Η επιλογή μεταξύ τους επηρεάζει την απόδοση και την κοκκομετρία του συγχρονισμού.

Synchronized μέθοδος

Επισημαίνοντας μια μέθοδο με τον τροποποιητή synchronized, την συγχρονίζετε αυτόματα στο τρέχον στιγμιότυπο (για συνηθισμένη μέθοδο) ή στο αντικείμενο Class (για στατική μέθοδο). Αυτός είναι ο απλούστερος τρόπος διασφάλισης αμοιβαίου αποκλεισμού, αλλά συχνά είναι υπερβολικός αν η κρίσιμη ενότητα αποτελεί μόνο ένα μικρό μέρος της μεθόδου και ο υπόλοιπος κώδικας δεν απαιτεί συγχρονισμό.

Synchronized μπλοκ

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

java
class DataProcessor {
    private final Object lock = new Object();

    public void process() {
        // κώδικας εκτός κρίσιμης ενότητας - χωρίς συγχρονισμό
        prepareData()

        synchronized (lock) {
            // μόνο αυτό το μπλοκ προστατεύεται
            updateSharedState()
        }

        // συνέχεια χωρίς κλείδωμα
        cleanup()
    }
}
ΚριτήριοSynchronized μέθοδοςSynchronized μπλοκ
Οθόνηthis (στιγμιότυπο) ή Classοποιοδήποτε αντικείμενο
Κοκκομετρίαολόκληρη η μέθοδοςμόνο ο απαραίτητος κώδικας
Αναγνωσιμότηταυψηλήμέτρια
Απόδοσηχαμηλότερη για μεγάλη μέθοδουψηλότερη για μικρή κρίσιμη ενότητα

Synchronized στο Android

Στην ανάπτυξη Android, το synchronized χρησιμοποιείται ευρέως για την προστασία SharedPreferences, πρόσβασης σε βάση δεδομένων και στοιχείων UI. Ωστόσο, η χρήση του στο κύριο νήμα δεν συνιστάται κατηγορηματικά λόγω κινδύνου παγώματος της διεπαφής.

Χρήση με SharedPreferences

Το SharedPreferences στο Android παρέχει βασική νηματική ασφάλεια, αλλά κατά την επεξεργασία από πολλαπλά νήματα μέσω Editor μπορεί να απαιτηθεί εξωτερικός συγχρονισμός. Ένα synchronized μπλοκ με ξεχωριστό αντικείμενο κλειδώματος εγγυάται τη συνέπεια των αλλαγών.

kotlin
class PreferencesManager(private val prefs: SharedPreferences) {
    private val lock = Any()

    fun writeToken(token: String) {
        synchronized (lock) {
            prefs.edit()
                .putString("auth_token", token)
                .apply()
        }
    }
}

Περιορισμοί σε εφαρμογές Android

Ο κύριος περιορισμός του synchronized στο Android — μπλοκάρισμα νήματος. Σε αντίθεση με τα coroutines με Mutex, το synchronized μπλοκάρει ολόκληρο το νήμα συστήματος. Στο κύριο νήμα, αυτό προκαλεί ANR. Στη σύγχρονη ανάπτυξη Android, συνιστάται η αντικατάσταση του synchronized με coroutines (suspend Mutex) ή ατομικούς τύπους (AtomicInteger).

Εναλλακτικές για synchronized

Η σύγχρονη Java και Kotlin προσφέρουν πολλές εναλλακτικές για το synchronized, καθεμία από τις οποίες λύνει τις ίδιες εργασίες με λιγότερους περιορισμούς ή καλύτερη απόδοση.

Lock από java.util.concurrent

Η διεπαφή Lock με υλοποιήσεις ReentrantLock και ReadWriteLock παρέχει χρονικά όρια, διακοπτόμενη αναμονή και πολλαπλές ουρές Condition. Είναι πιο ευέλικτη από το synchronized, αλλά απαιτεί ρητή απελευθέρωση στο finally, αυξάνοντας τον κίνδυνο σφάλματος σε περίπτωση ξεχασμένου unlock.

Ατομικές κλάσεις

Οι κλάσεις AtomicInteger, AtomicLong, AtomicReference και άλλες χρησιμοποιούν Lock-Free αλγόριθμους βασισμένους σε CAS (Compare-And-Swap). Είναι σημαντικά ταχύτερες από το synchronized σε σενάρια με μέτριο ανταγωνισμό, καθώς δεν μπλοκάρουν νήματα, αλλά εκτελούν αισιόδοξες επαναληπτικές προσπάθειες και δεν απαιτούν εναλλαγή περιβάλλοντος από τον πυρήνα του OS.

ThreadLocal και ασφάλεια νημάτων

Το ThreadLocal παρέχει μια εναλλακτική προσέγγιση: κάθε μεταβλητή ThreadLocal είναι απομονωμένη στο πλαίσιο ενός νήματος και δεν απαιτεί συγχρονισμό για ανάγνωση και εγγραφή. Αυτό εξαλείφει εντελώς την ανάγκη για synchronized για δεδομένα που δεν πρέπει να μοιράζονται μεταξύ νημάτων. Το ThreadLocal χρησιμοποιείται ενεργά σε frameworks (Spring, Hibernate) για αποθήκευση περιβάλλοντος συναλλαγών και συνεδριών.

Coroutines και προσέγγιση compose

Σε έργα Kotlin για Android, η εναλλακτική για το synchronized είναι το Mutex από kotlinx.coroutines. Δεν μπλοκάρει το νήμα του λειτουργικού συστήματος, αλλά αναστέλλει το coroutine μέχρι την απελευθέρωση του κλειδώματος — αυτό επιτρέπει την αποτελεσματική χρήση νημάτων από το pool και αποφυγή ANR κατά τη μακρά αναμονή για απελευθέρωση πόρου.

kotlin
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

val mutex = Mutex()
var counter = 0

suspend fun safeIncrement() {
    mutex.withLock {
        counter++
    }
}

Απόδοση του synchronized

Η απόδοση του synchronized έχει αλλάξει σημαντικά στις πρόσφατες εκδόσεις Java. Παλαιότερα θεωρούνταν „βαρύς„ μηχανισμός, αλλά οι σύγχρονες JVM έχουν εξαλείψει το μεγαλύτερο μέρος της επιβάρυνσης χάρη στις προηγμένες βελτιστοποιήσεις του μεταγλωττιστή JIT. Ας εξετάσουμε λεπτομερώς πώς η εικονική μηχανή επιταχύνει τον συγχρονισμένο κώδικα κατά τον χρόνο εκτέλεσης.

Biased Locking και Lock Coarsening

Ο μεταγλωττιστής JIT της JVM εφαρμόζει διάφορες βελτιστοποιήσεις: biased locking εξαλείφει τον συγχρονισμό αν το κλείδωμα καταλαμβάνεται πάντα από ένα νήμα; lock coarsening συνενώνει γειτονικά synchronized μπλοκ σε ένα; lock elimination αφαιρεί τον συγχρονισμό αν το αντικείμενο είναι προσβάσιμο μόνο από ένα νήμα. Αυτές οι βελτιστοποιήσεις καθιστούν το synchronized πρακτικά δωρεάν σε χαμηλό ανταγωνισμό.

Μέτρηση ανταγωνισμού και επιλογή βελτιστοποίησης

Η JVM καθορίζει το επίπεδο ανταγωνισμού για κάθε αντικείμενο: σε απουσία ανταγωνισμού ενεργοποιείται το biased locking, με την εμφάνιση δεύτερου νήματος το κλείδωμα μεταβαίνει σε λειτουργία lightweight με spin-waiting, και μόνο σε παρατεταμένη αναμονή — σε heavyweight με mutex συστήματος. Αυτή η κλιμάκωση γίνεται αυτόματα και ο προγραμματιστής δεν χρειάζεται να επιλέξει χειροκίνητα στρατηγική.

Σύγκριση με Lock και ατομικές κλάσεις

Σε σύγχρονες μετρήσεις (Java 17+) το synchronized εμφανίζει απόδοση συγκρίσιμη με το ReentrantLock σε χαμηλό και μέτριο ανταγωνισμό. Σε υψηλό ανταγωνισμό, το Lock μπορεί να έχει πλεονέκτημα χάρη στην αποτελεσματικότερη ουρά αναμονής με υποστήριξη χρονικών ορίων και διακοπών. Για συστήματα υψηλού φόρτου όπου ο ανταγωνισμός είναι σταθερός, το ReentrantLock σε λειτουργία fair παρέχει πιο προβλέψιμη συμπεριφορά.

Οι ατομικές κλάσεις (AtomicInteger, AtomicReference) παραμένουν οι ταχύτερες για απλούς μετρητές και σημαίες χάρη στην υλοποίηση Lock-Free σε CAS. Δεν μπλοκάρουν καθόλου τα νήματα — σε περίπτωση σύγκρουσης, η λειτουργία απλώς επαναλαμβάνεται σε έναν βρόχο. Αυτό δίνει βελτίωση απόδοσης 3-5 φορές σε σύγκριση με το synchronized σε λειτουργίες αύξησης μετρητή σε 4-8 νήματα.

Συχνές ερωτήσεις

Ποια είναι η διαφορά μεταξύ synchronized και volatile;

Synchronized παρέχει τόσο αμοιβαίο αποκλεισμό όσο και ορατότητα. Το Volatile εγγυάται μόνο την ορατότητα αλλαγών — η εγγραφή σε μια volatile μεταβλητή είναι ορατή σε όλα τα νήματα, αλλά δεν αποτρέπει την ταυτόχρονη τροποποίηση, δηλαδή δεν προστατεύει από συνθήκη ανταγωνισμού.

Μπορεί το synchronized να προκαλέσει deadlock;

Ναι, deadlock είναι πιθανός σε ένθετο συγχρονισμό με διαφορετική σειρά οθονών. Για παράδειγμα, ένα νήμα καλεί synchronized(a) { synchronized(b) }, και ένα άλλο — synchronized(b) { synchronized(a) }. Αποφύγετε ένθετα synchronized μπλοκ ή καθορίστε ενιαία σειρά οθονών.

Τι είναι η οθόνη στη Java;

Οθόνη είναι ένας μηχανισμός συγχρονισμού που σχετίζεται με κάθε αντικείμενο Java. Εγγυάται ότι μόνο ένα νήμα εκτελεί synchronized κώδικα σε αυτό το αντικείμενο. Η οθόνη περιλαμβάνει κλείδωμα, ουρά αναμονής και σύνολο νημάτων που περιμένουν ειδοποίηση μέσω wait/notify.

Είναι το Lock ταχύτερο από το synchronized;

Σε σύγχρονες εκδόσεις Java (17+) το synchronized δεν υστερεί έναντι του Lock σε απόδοση χάρη στις βελτιστοποιήσεις JIT (biased locking, lock coarsening). Το Lock προτιμάται όχι λόγω ταχύτητας, αλλά λόγω πρόσθετων δυνατοτήτων: χρονικά όρια, διακοπτόμενη αναμονή και πολλαπλές Condition.

Πώς λειτουργεί το synchronized με στατικές μεθόδους;

Στατική synchronized μέθοδος χρησιμοποιεί την οθόνη του αντικειμένου Class αυτής της κλάσης, όχι του στιγμιοτύπου. Αυτό σημαίνει ότι ο συγχρονισμός καλύπτει όλα τα στιγμιότυπα της κλάσης. Οι μη στατικές και στατικές synchronized μέθοδοι χρησιμοποιούν διαφορετικές οθόνες και δεν μπλοκάρουν η μία την άλλη.

Σύνοψη

  • Synchronized — ενσωματωμένος μηχανισμός συγχρονισμού Java βασισμένος σε οθόνες.
  • Οθόνη αντικειμένου — εσωτερική δομή JVM που διασφαλίζει αμοιβαίο αποκλεισμό.
  • Μέθοδοι wait/notify χρησιμοποιούνται μόνο εντός synchronized μπλοκ ή μεθόδων.
  • Synchronized μπλοκ προτιμάται έναντι μεθόδου λόγω λεπτότερης κοκκομετρίας συγχρονισμού.
  • Happens-before εγγυάται ορατότητα αλλαγών μεταξύ νημάτων σε συγχρονισμό στο ίδιο αντικείμενο.
  • Deadlock — κύριος κίνδυνος σε ένθετο συγχρονισμό με διαφορετική σειρά οθονών.
  • Εναλλακτικές — Lock, ατομικές κλάσεις και coroutines με suspending Mutex.

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης