CodeGym /مدونة جافا /Random-AR /مزامنة الموضوع. المشغل المتزامن
John Squirrels
مستوى
San Francisco

مزامنة الموضوع. المشغل المتزامن

نشرت في المجموعة
أهلاً! سنستمر اليوم في النظر في ميزات البرمجة متعددة الخيوط والحديث عن مزامنة الخيوط. مزامنة الموضوع.  المشغل المتزامن - 1

ما هي المزامنة في جافا؟

خارج مجال البرمجة، فهو يتضمن ترتيبًا يسمح لجهازين أو برنامجين بالعمل معًا. على سبيل المثال، يمكن مزامنة الهاتف الذكي والكمبيوتر مع حساب Google، ويمكن مزامنة حساب موقع الويب مع حسابات الشبكة الاجتماعية حتى تتمكن من استخدامها لتسجيل الدخول. مزامنة سلسلة المحادثات لها معنى مشابه: إنها ترتيب تتفاعل فيه سلاسل الرسائل مع بعضها البعض. في الدروس السابقة عاشت خيوطنا وعملت بشكل منفصل عن بعضها البعض. أجرى أحدهم عملية حسابية، ونام الثاني، وعرض ثالث شيئًا ما على وحدة التحكم، لكنهم لم يتفاعلوا. في البرامج الحقيقية، مثل هذه الحالات نادرة. يمكن لخيوط متعددة العمل بنشاط مع نفس مجموعة البيانات وتعديلها. وهذا يخلق مشاكل. تخيل أن هناك عدة سلاسل تكتب نصًا في نفس المكان، على سبيل المثال، إلى ملف نصي أو وحدة التحكم. في هذه الحالة، يصبح الملف أو وحدة التحكم موردًا مشتركًا. الخيوط غير مدركة لوجود بعضها البعض، لذا فهي ببساطة تكتب كل ما في وسعها في الوقت المخصص لها بواسطة برنامج جدولة الخيوط. في درس حديث، رأينا مثالاً على ما يؤدي إليه هذا. لنتذكرها الآن: مزامنة الموضوع.  المشغل المتزامن - 2السبب يكمن في حقيقة أن الخيوط تعمل مع مورد مشترك (وحدة التحكم) دون تنسيق أفعالها مع بعضها البعض. إذا قام برنامج جدولة الخيط بتخصيص الوقت لـ Thread-1، فإنه يكتب كل شيء على الفور إلى وحدة التحكم. ما هي المواضيع الأخرى التي تمكنت أو لم تتمكن من كتابتها بالفعل لا يهم. والنتيجة كما ترون محبطة. ولهذا السبب قدموا مفهومًا خاصًا، وهو كائن المزامنة (الاستبعاد المتبادل) ، للبرمجة متعددة الخيوط. الغرض من كائن المزامنة (mutex) هو توفير آلية بحيث يتمكن مؤشر ترابط واحد فقط من الوصول إلى كائن ما في وقت معين. إذا حصل Thread-1 على كائن المزامنة للكائن A، فلن تتمكن سلاسل الرسائل الأخرى من الوصول إلى الكائن وتعديله. يجب أن تنتظر مؤشرات الترابط الأخرى حتى يتم تحرير كائن المزامنة الخاص بالكائن A. إليك مثال من الحياة: تخيل أنك و10 غرباء آخرين تشاركون في تمرين. بالتناوب، تحتاج إلى التعبير عن أفكارك ومناقشة شيء ما. ولكن نظرًا لأنكما ترى بعضكما البعض لأول مرة، وحتى لا تقاطعا بعضكما البعض باستمرار وتغضبا، فإنكما تستخدمان "كرة التحدث": الشخص الذي لديه الكرة فقط يمكنه التحدث. بهذه الطريقة ينتهي بك الأمر إلى إجراء مناقشة جيدة ومثمرة. في الأساس، الكرة هي كائن المزامنة (mutex). إذا كان كائن المزامنة في يد مؤشر ترابط واحد، فلن تتمكن مؤشرات الترابط الأخرى من العمل مع الكائن. لا تحتاج إلى القيام بأي شيء لإنشاء كائن المزامنة (mutex): فهو مدمج بالفعل في الفئة Object، مما يعني أن كل كائن في Java لديه واحد.

كيف يعمل المشغل المتزامن

دعونا نتعرف على كلمة رئيسية جديدة: syncronized . يتم استخدامه لتمييز كتلة معينة من التعليمات البرمجية. إذا تم تمييز كتلة التعليمات البرمجية بالكلمة synchronizedالأساسية، فلا يمكن تنفيذ هذه الكتلة إلا بواسطة مؤشر ترابط واحد في المرة الواحدة. يمكن تنفيذ المزامنة بطرق مختلفة. على سبيل المثال، من خلال الإعلان عن طريقة كاملة للمزامنة:
public synchronized void doSomething() {

   // ...Method logic
}
أو اكتب كتلة تعليمات برمجية حيث يتم إجراء المزامنة باستخدام كائن ما:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...Some logic available simultaneously to all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
المعنى بسيط. إذا دخل أحد الخيوط داخل كتلة التعليمات البرمجية المميزة بالكلمة synchronizedالأساسية، فإنه يلتقط كائن المزامنة للكائن على الفور، وتضطر جميع الخيوط الأخرى التي تحاول إدخال نفس الكتلة أو الطريقة إلى الانتظار حتى يكمل الخيط السابق عمله ويحرر الشاشة. مزامنة الموضوع.  المشغل المتزامن - 3بالمناسبة! خلال الدورة التدريبية، كنت قد شاهدت بالفعل أمثلة على synchronized، لكنها بدت مختلفة:
public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
الموضوع جديد بالنسبة لك . وبطبيعة الحال، سيكون هناك ارتباك مع بناء الجملة. لذا، احفظه على الفور لتجنب الخلط لاحقًا بين الطرق المختلفة لكتابته. هاتان الطريقتان للكتابة تعنيان نفس الشيء:
public void swap() {

   synchronized (this)
   {
       // ...Method logic
   }
}


public synchronized void swap() {

   }
}
في الحالة الأولى، تقوم بإنشاء كتلة متزامنة من التعليمات البرمجية فور إدخال الطريقة. تتم مزامنته بواسطة thisالكائن، أي الكائن الحالي. وفي المثال الثاني، قمت بتطبيق synchronizedالكلمة الأساسية على الطريقة بأكملها. وهذا يجعل من غير الضروري الإشارة بوضوح إلى الكائن المستخدم للمزامنة. نظرًا لأنه تم تمييز الطريقة بأكملها بالكلمة الأساسية، فستتم مزامنة الطريقة تلقائيًا لجميع مثيلات الفئة. لن نتعمق في النقاش حول الطريقة الأفضل. في الوقت الحالي، اختر الطريقة التي تفضلها :) الشيء الرئيسي هو أن تتذكر: لا يمكنك الإعلان عن طريقة متزامنة إلا عندما يتم تنفيذ كل منطقها بواسطة مؤشر ترابط واحد في المرة الواحدة. على سبيل المثال، سيكون من الخطأ مزامنة doSomething()الطريقة التالية:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...Some logic available simultaneously to all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
كما ترى، يحتوي جزء من الطريقة على منطق لا يتطلب المزامنة. يمكن تشغيل هذا الرمز بواسطة عدة سلاسل رسائل في نفس الوقت، ويتم فصل جميع الأماكن المهمة في synchronizedكتلة منفصلة. وهناك شيئ اخر. دعونا نفحص عن كثب مثالنا من الدرس مع تبديل الأسماء:
public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
ملحوظة: تتم المزامنة باستخدامthis. وهذا هو، باستخدام كائن معينMyClass. لنفترض أن لدينا خيطين (Thread-1وThread-2) وكائنًا واحدًا فقطMyClass myClass. في هذه الحالة، إذاThread-1تم استدعاءmyClass.swap()الأسلوب، فسيكون كائن المزامنة للكائن مشغولاً، وعند محاولة استدعاء الأسلوب،myClass.swap()سيتمThread-2تعليقه أثناء انتظار تحرير كائن المزامنة. إذا كان لدينا خيطين وكائنينMyClass(myClass1وmyClass2)، فيمكن لخيوطنا بسهولة تنفيذ الأساليب المتزامنة على كائنات مختلفة في وقت واحد. الخيط الأول ينفذ هذا:
myClass1.swap();
الثاني ينفذ هذا:
myClass2.swap();
في هذه الحالة، لن تؤثر synchronizedالكلمة الأساسية الموجودة داخل swap()الطريقة على تشغيل البرنامج، حيث يتم إجراء المزامنة باستخدام كائن معين. وفي الحالة الأخيرة، لدينا شيئين. وبالتالي فإن الخيوط لا تخلق مشاكل لبعضها البعض. بعد كل شيء، هناك كائنان لهما كائنان مختلفان، والحصول على أحدهما مستقل عن الحصول على الآخر .

الميزات الخاصة للمزامنة في الطرق الثابتة

ولكن ماذا لو كنت بحاجة إلى مزامنة طريقة ثابتة ؟
class MyClass {
   private static String name1 = "Ally";
   private static String name2 = "Lena";

   public static synchronized void swap() {
       String s = name1;
       name1 = name2;
       name2 = s;
   }

}
ليس من الواضح ما هو الدور الذي سيلعبه كائن المزامنة (mutex) هنا. بعد كل شيء، لقد قررنا بالفعل أن كل كائن لديه كائن المزامنة (mutex). لكن المشكلة هي أننا لا نحتاج إلى كائنات لاستدعاء الطريقة MyClass.swap(): فالطريقة ثابتة! إذا ما هو التالي؟ :/ لا توجد مشكلة هنا في الواقع. اعتنى منشئو Java بكل شيء :) إذا كانت الطريقة التي تحتوي على منطق متزامن مهم ثابتة، فسيتم إجراء المزامنة على مستوى الفصل. لمزيد من الوضوح، يمكننا إعادة كتابة الكود أعلاه على النحو التالي:
class MyClass {
   private static String name1 = "Ally";
   private static String name2 = "Lena";

   public static void swap() {

       synchronized (MyClass.class) {
           String s = name1;
           name1 = name2;
           name2 = s;
       }
   }

}
من حيث المبدأ، كان من الممكن أن تفكر في هذا بنفسك: نظرًا لعدم وجود كائنات، يجب دمج آلية المزامنة بطريقة ما في الفصل نفسه. وهذا هو الحال: يمكننا استخدام الفئات للمزامنة.
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION