JavaRush /مدونة جافا /Random-AR /مزامنة الموضوع. عامل متزامن في جافا

مزامنة الموضوع. عامل متزامن في جافا

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

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

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

   //...method logic
}
أو اكتب كتلة من التعليمات البرمجية حيث تتم المزامنة على كائن ما:
public class Main {

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

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

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


public synchronized void swap() {

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

   private Object obj = new Object();

   public void doSomething() {

       //...some logic available to all threads

       synchronized (obj) {

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

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

ولكن ماذا لو كنت بحاجة إلى مزامنة طريقة ثابتة؟
class MyClass {
   private static String name1 = "Olya";
   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 = "Olya";
   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