Synchronized: ما هو، مبدأ العمل والاستخدام في Java

المؤلف: IT Sectr نُشر: 2026-03-19 وقت القراءة: 8 دق

Synchronized هي آلية مزامنة مدمجة في لغة Java توفر وصولاً حصرياً إلى الأقسام الحرجة من الكود. وفقاً لـ Oracle، 2024، يضمن المعدل synchronized أن خيطاً واحداً فقط يمكنه تنفيذ الطريقة أو الكتلة المحددة في لحظة زمنية محددة. تعتمد هذه الآلية على الشاشات (monitors) — مفهوم أساسي في أنظمة التشغيل يضمن التشغيل الصحيح للتطبيقات متعددة الخيوط بجميع مستويات التعقيد.

الملخص

  • Synchronized — كلمة مفتاحية في Java للوصول الآمن للبيانات بين الخيوط.
  • مراقب الكائن — الآلية الداخلية التي تقوم عليها المزامنة.
  • طريقة synchronized تقفل الطريقة بأكملها على مستوى المثيل أو الفئة.
  • كتلة synchronized تسمح بمزامنة جزء فقط من الكود.
  • Deadlock — أحد المشاكل الرئيسية في المزامنة المتداخلة.

ما هو synchronized؟

Synchronized هي كلمة مفتاحية في Java تضمن أن خيطاً واحداً فقط ينفذ مقطعاً محمياً من الكود في أي وقت، مما يمنع تلف البيانات أثناء الوصول المتزامن. ظهرت في الإصدار الأول من Java ولا تزال أبسط طريقة لضمان أمان الخيوط للمطورين من أي مستوى خبرة.

التعريف والدور في Java

يحل المعدل synchronized مهمتين: الاستبعاد المتبادل و رؤية التغييرات. عندما يخرج خيط من كتلة synchronized، تكون جميع التغييرات مرئية للخيوط الأخرى التي تدخل كتلة متزامنة على نفس الكائن.

يمكن تطبيق synchronized على طريقة كاملة أو على كتلة كود عشوائية مع تحديد كائن المراقب. في كلتا الحالتين، تقوم JVM بإدراج تعليمات monitorenter و monitorexit على مستوى البايت كود.

دوافع الظهور

في التطبيقات متعددة الخيوط دون مزامنة، تحدث حالة تسابق (race condition) عندما يقوم خيطان بتعديل نفس البيانات في وقت واحد، مما يؤدي إلى نتائج غير متوقعة. أصبح synchronized الأداة الأولى والرئيسية في Java لمكافحة هذه المشكلة، حيث يوفر بناء جملة تصريحي بسيط يمكن لأي مطور استخدامه.

كيف يعمل synchronized؟

تعتمد آلية synchronized على مفهوم الشاشة (monitor) — وهو بدائي مزامنة عالي المستوى مدمج في كل كائن 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 والوصول إلى قواعد البيانات ومكونات واجهة المستخدم. ومع ذلك، فإن استخدامه في الخيط الرئيسي غير موصى به بشدة بسبب خطر تجميد الواجهة.

الاستخدام مع 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 في سيناريوهات التنافس المعتدل لأنها لا تحظر الخيوط بل تقوم بإعادة محاولات متفائلة دون الحاجة إلى تبديل سياق نواة نظام التشغيل.

ThreadLocal وأمان الخيوط

يوفر ThreadLocal نهجاً بديلاً: كل متغير ThreadLocal معزول داخل خيط واحد ولا يتطلب مزامنة للقراءة والكتابة. هذا يلغي تماماً الحاجة إلى synchronized للبيانات التي لا ينبغي مشاركتها بين الخيوط. يُستخدم ThreadLocal بنشاط في الأطر (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 الأخيرة. كان يُعتبر في السابق آلية “ثقيلة”، لكن JVMs الحديثة أزالت معظم الحمل الزائد من خلال تحسينات متقدمة لمترجم JIT. دعونا نلقي نظرة مفصلة على كيفية تسريع الآلة الافتراضية للكود المتزامن في وقت التشغيل.

Biased Locking و Lock Coarsening

يطبق مترجم JIT في JVM عدة تحسينات: biased locking يلغي المزامنة إذا كان القفل يُستحوذ عليه دائماً بواسطة نفس الخيط؛ lock coarsening يدمج كتل synchronized المتجاورة في كتلة واحدة؛ lock elimination يزيل المزامنة إذا كان الكائن يمكن الوصول إليه بواسطة خيط واحد فقط. هذه التحسينات تجعل synchronized مجانياً تقريباً عند التنافس المنخفض.

قياس التنافس واختيار التحسين

تحدد JVM مستوى التنافس لكل كائن: عندما لا يكون هناك تنافس، يتم تفعيل biased locking؛ عندما يظهر خيط ثانٍ، ينتقل القفل إلى الوضع الخفيف مع انتظار دوراني (spin)؛ وفقط أثناء الانتظار الطويل يتصاعد إلى الوضع الثقيل مع mutex لنظام التشغيل. يحدث هذا التصعيد تلقائياً، ولا يحتاج المطور إلى اختيار استراتيجية يدوياً.

مقارنة مع Lock والفئات الذرية

في المقاييس الحديثة (Java 17+)، يُظهر synchronized أداءً مماثلاً لـ ReentrantLock عند التنافس المنخفض والمعتدل. عند التنافس العالي، قد يكون Lock أفضل بفضل قائمة انتظار أكثر كفاءة مع دعم المهلات الزمنية والمقاطعة. للأنظمة عالية التحميل حيث التنافس ثابت، يوفر ReentrantLock مع الوضع العادل (fair) سلوكاً أكثر قابلية للتنبؤ.

تبقى الفئات الذرية (AtomicInteger, AtomicReference) الأسرع للعدادات والأعلام البسيطة بفضل تنفيذ Lock-Free القائم على CAS. لا تحظر الخيوط على الإطلاق — عند حدوث تعارض، يتم simply إعادة محاولة العملية في حلقة. يعطي هذا زيادة في الأداء تتراوح من 3 إلى 5 مرات مقارنة بـ synchronized في عمليات زيادة العداد مع 4-8 خيوط.

الأسئلة الشائعة

ما الفرق بين synchronized و volatile؟

Synchronized يوفر كلاً من الاستبعاد المتبادل والرؤية. Volatile يضمن فقط رؤية التغييرات — الكتابة إلى متغير volatile مرئية لجميع الخيوط لكنها لا تمنع التعديل المتزامن، أي أنها لا تحمي من حالة التسابق.

هل يمكن أن يسبب synchronized deadlock؟

نعم، deadlock ممكن مع المزامنة المتداخلة باستخدام ترتيب مختلف للشاشات. على سبيل المثال، خيط يستدعي synchronized(a) { synchronized(b) }، بينما آخر يستدعي synchronized(b) { synchronized(a) }. تجنب كتل synchronized المتداخلة أو حدد ترتيباً ثابتاً للشاشات.

ما هي الشاشة (monitor) في 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. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا