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 را در سطح بایت‌کد وارد می‌کند.

دلایل ظهور

در برنامه‌های چندرشته‌ای بدون همگام‌سازی، شرایط رقابتی (race condition) رخ می‌دهد — زمانی که دو رشته به طور همزمان داده‌های یکسانی را تغییر می‌دهند و به نتایج غیرقابل پیش‌بینی منجر می‌شوند. Synchronized اولین و اصلی‌ترین ابزار Java برای مقابله با این مشکل شد و نحوی ساده و اعلامی را ارائه کرد که برای هر توسعه‌دهنده‌ای قابل دسترس است.

Synchronized چگونه کار می‌کند؟

مکانیزم synchronized بر مفهوم مانیتور استوار است — یک اولیه همگام‌سازی سطح بالا که در هر شیء Java تعبیه شده است. مانیتور هنگام اولین استفاده از بلوک synchronized روی یک شیء به آن متصل می‌شود.

مانیتور شیء

هر شیء در Java یک مانیتور مرتبط دارد. وقتی یک رشته وارد بلوک synchronized می‌شود، مانیتور شیء را تصاحب می‌کند. اگر مانیتور قبلاً توسط رشته دیگری اشغال شده باشد، رشته تا زمان آزادسازی مسدود می‌شود. در بایت‌کد، این با جفت دستورالعمل monitorenter و monitorexit مطابقت دارد.

حالت‌های قفل (biased locking)

JVM synchronized را از طریق چند سطح بهینه می‌کند: biased locking (قفل متمایل) برای دسترسی تک‌رشته‌ای، lightweight locking (قفل سبک) برای رقابت کم و heavyweight locking (قفل سنگین) برای رقابت شدید با مشارکت سیستم‌عامل. این سطوح بدون تغییر کد، عملکرد را افزایش می‌دهند.

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 در مقابل بلوک

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 — مسدود کردن رشته. برخلاف کوروتین‌ها با Mutex، synchronized کل رشته سیستم را مسدود می‌کند. در رشته اصلی این باعث ANR می‌شود. در توسعه مدرن Android توصیه می‌شود synchronized با کوروتین‌ها (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 هستند زیرا رشته‌ها را مسدود نمی‌کنند، بلکه تلاش‌های مجدد خوش‌بینانه انجام می‌دهند و نیازی به تغییر زمینه توسط هسته سیستم‌عامل ندارند.

ThreadLocal و ایمنی رشته‌ها

ThreadLocal یک رویکرد جایگزین ارائه می‌دهد: هر متغیر ThreadLocal در چارچوب یک رشته ایزوله شده و برای خواندن و نوشتن نیازی به همگام‌سازی ندارد. این نیاز به synchronized را برای داده‌هایی که نباید بین رشته‌ها به اشتراک گذاشته شوند کاملاً از بین می‌برد. ThreadLocal به طور فعال در فریمورک‌ها (Spring، Hibernate) برای ذخیره زمینه تراکنش‌ها و جلسات استفاده می‌شود.

کوروتین‌ها و رویکرد compose

در پروژه‌های Kotlin برای Android، جایگزین synchronized Mutex از kotlinx.coroutines است. این رشته سیستم عامل را مسدود نمی‌کند، بلکه کوروتین را تا زمان آزاد شدن قفل معلق می‌کند — این امکان استفاده مؤثر از رشته‌های حوضچه و جلوگیری از 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 سیستم. این escalation به طور خودکار رخ می‌دهد و توسعه‌دهنده نیازی به انتخاب دستی استراتژی ندارد.

مقایسه با 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 به لطف بهینه‌سازی‌های JIT (biased locking, lock coarsening) از نظر عملکرد از Lock عقب نمی‌ماند. Lock نه به خاطر سرعت، بلکه به دلیل قابلیت‌های اضافی ترجیح داده می‌شود: مهلت زمانی، انتظار قابل قطع و چندین Condition.

Synchronized با متدهای ایستا چگونه کار می‌کند؟

متد ایستای synchronized از مانیتور شیء Class آن کلاس استفاده می‌کند، نه نمونه. این بدان معناست که همگام‌سازی تمام نمونه‌های کلاس را پوشش می‌دهد. متدهای غیرایستا و ایستای synchronized از مانیتورهای متفاوتی استفاده می‌کنند و یکدیگر را مسدود نمی‌کنند.

خلاصه

  • Synchronized — مکانیزم همگام‌سازی داخلی Java مبتنی بر مانیتورها.
  • مانیتور شیء — ساختار داخلی JVM که انحصار متقابل را تضمین می‌کند.
  • متدهای wait/notify فقط در داخل بلوک‌ها و متدهای synchronized استفاده می‌شوند.
  • بلوک synchronized به دلیل دانه‌بندی دقیق‌تر همگام‌سازی بر متد ارجحیت دارد.
  • Happens-before قابلیت مشاهده تغییرات بین رشته‌ها را در همگام‌سازی روی یک شیء تضمین می‌کند.
  • Deadlock — ریسک اصلی در همگام‌سازی تو در تو با ترتیب متفاوت مانیتورها.
  • جایگزین‌ها — Lock، کلاس‌های اتمی و کوروتین‌ها با suspending Mutex.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید