تزامن مؤشر ترابط Java <br /> عندما يحتاج اثنين أو أكثر إلى مشاركة الموارد ، فإنهم يحتاجون إلى طريقة ما لتحديد أن المورد يشغله موضوع واحد فقط في لحظة معينة. تسمى عملية تحقيق هذا الهدف المزامنة. كما ترون ، توفر Java دعمًا فريدًا على مستوى اللغة لهذا.
مفتاح التزامن هو مفهوم خط الأنابيب (يسمى أيضًا Semaphore). mutex هو كائن مغلق mutex ، أو mutex. في وقت معين ، يمكن لخيط واحد فقط الحصول على عملية الإدارة. عندما يحتاج الخيط إلى قفل ، يجب أن يدخل خط الأنابيب. يجب تعليق جميع المواضيع الأخرى التي تحاول إدخال الأنبوب المقفل حتى يخرج الخيط الأول من الأنبوب. وتسمى هذه المواضيع الأخرى عمليات إدارة الانتظار. يمكن لخيط مع عملية الإدارة إدخال نفس عملية الإدارة مرة أخرى إذا أرادت.
إذا كنت قد استخدمت التزامن بلغات أخرى مثل C أو C ++ ، فستعرف أنه من الغريب بعض الشيء الاستخدام. وذلك لأن العديد من اللغات لا تدعم التزامن نفسها. بدلاً من ذلك ، بالنسبة للخيوط المتزامنة ، يجب أن تستخدم البرامج لغة مصدر نظام التشغيل. لحسن الحظ ، تنفذ Java التزامن من خلال عناصر اللغة ، ويتم القضاء على معظم التعقيدات المتعلقة بالتزامن.
يمكنك مزامنة الكود بطريقتين. يشمل كلاهما استخدام الكلمات الرئيسية المتزامنة ، والما يلي هما الطريقتان.
باستخدام طريقة التزامن
التزامن في Java بسيط لأن جميع الكائنات لها إجراءاتها الضمنية المقابلة. إن إدخال عملية الإدارة لكائن معين هو استدعاء الطريقة المعدلة بواسطة الكلمة الرئيسية المتزامنة. عندما يكون مؤشر ترابط داخل طريقة المزامنة ، يجب أن تنتظر جميع مؤشرات الترابط الأخرى في نفس المثال الذي يحاول استدعاء الطريقة (أو طريقة التزامن الأخرى). من أجل الخروج من عملية الإدارة والتخلي عن السيطرة على الكائن إلى مؤشرات ترابط الانتظار الأخرى ، تحتاج مؤشرات الترابط مع عمليات الإدارة فقط إلى العودة من طريقة التزامن.
لفهم ضرورة التزامن ، لنبدأ بمثال بسيط حيث يجب استخدام التزامن ولكنه غير مفيد. البرنامج التالي لديه ثلاث فصول بسيطة. الأول هو CallMe ، الذي لديه طريقة بسيطة للاتصال (). تحتوي طريقة Call () على معلمة سلسلة تدعى MSG. تحاول هذه الطريقة طباعة سلسلة MSG بين قوسين مربعة. الشيء المثير للاهتمام هو أنه بعد استدعاء الاتصال () يطبع القوس الأيسر وسلسلة MSG ، تم استدعاء Thread.sleep (1000) ، والذي يتوقف مؤقتًا عن الخيط الحالي لمدة ثانية واحدة.
يشير مُنشئ المتصل بالفئة التالية إلى مثيل CallMe وسلسلة ، موجودة في Target و MSG على التوالي. يقوم المنشئ أيضًا بإنشاء مؤشر ترابط جديد يستدعي طريقة Run () للكائن. يبدأ الموضوع على الفور. تستدعي طريقة Run () لفئة المتصل طريقة Call () هدف مثيل CallMe من خلال سلسلة MSG. أخيرًا ، تبدأ فئة Synch بإنشاء مثيل بسيط من CallMe وثلاث مثيلات من المتصل مع سلاسل رسائل مختلفة.
يتم تمرير نفس مثيل Callme إلى كل مثيل المتصل.
// لا يتم مزامنة هذا البرنامج. out .println ("المقاطعات") ؛ TARG ؛ Cal LME Target = Callme () ؛ انتظر threads {ob1.t.join () ؛ }}
إخراج هذا البرنامج كما يلي:
مرحبا [متزامن [العالم]]]]
في هذا المثال ، من خلال استدعاء Sleep () ، تسمح طريقة Call () بتنفيذ التنفيذ إلى مؤشر ترابط آخر. هذه النتيجة هي إخراج مختلط من ثلاثة سلاسل الرسائل. في هذا البرنامج ، لا توجد طريقة تمنع ثلاثة مؤشرات ترابط من استدعاء نفس الطريقة للكائن نفسه في نفس الوقت. هذه مسابقة لأن ثلاثة مؤشرات ترابط تتنافس لإكمال الطريقة. يستخدم المثال Sleep () لجعل التأثير يتكرر وواضح. في معظم الحالات ، تكون المنافسة أكثر تعقيدًا ولا يمكن التنبؤ بها لأنه لا يمكنك التأكد من حدوث تحويل السياق. هذا يتسبب في تشغيل البرنامج بشكل طبيعي وأحيانًا خطأ.
لتحقيق الغرض المطلوب في المثال أعلاه ، يجب أن يكون لديك الحق في استخدام Call (). هذا هو ، في مرحلة ما ، يجب أن يقتصر على موضوع واحد فقط يمكنه التحكم فيه. للقيام بذلك ، تحتاج فقط إلى تقديم تعريف Call () متزامن ، على النحو التالي:
class callme {synchronized void call (String msg) {...
هذا يمنع مؤشرات الترابط الأخرى من إدخال Call () عندما يستخدم مؤشر ترابط واحد Call (). بعد إضافة مزامنة إلى Call () ، يخرج البرنامج على النحو التالي:
[مرحبا] [متزامن] [العالم]
في أي وقت في مواقف الرئاسة المتعددة ، إذا كان لديك طريقة واحدة أو طرق متعددة لمعالجة الحالة الداخلية للكائن ، فيجب عليك استخدام الكلمة الرئيسية المتزامنة لمنع منافسة الدولة. تذكر أنه بمجرد دخول مؤشر ترابط إلى طريقة التزامن على المثيل ، لا يمكن لأي مؤشر ترابط آخر إدخال طريقة التزامن في نفس الحالة. ومع ذلك ، لا يزال من الممكن استدعاء أساليب ASYNC الأخرى في هذه الحالة.
بيانات متزامنة
على الرغم من أن إنشاء طريقة التزامن داخل فئة تم إنشاؤها هو وسيلة بسيطة وفعالة للحصول على المزامنة ، إلا أنها ليست فعالة في جميع الأوقات. يرجى التفكير في الأسباب الكامنة وراء هذا. لنفترض أنك ترغب في الحصول على وصول متزامن إلى كائنات الفئة غير المصممة للوصول متعدد الترابطات ، أي أن الفئة لا تستخدم الطريقة المتزامنة. علاوة على ذلك ، لا يتم إنشاء الفصل من قبلك ، ولكن من خلال طرف ثالث ، ولا يمكنك الحصول على رمز المصدر الخاص به. وبهذه الطريقة ، لا يمكنك إعداد المعدل المتزامن على الطريقة ذات الصلة. كيف يمكن مزامنة كائن من هذه الفئة؟ لحسن الحظ ، فإن الحل بسيط: تحتاج فقط إلى وضع المكالمة على الطريقة المحددة بواسطة هذه الفئة في كتلة متزامنة.
هنا هو الشكل الطبيعي للبيان المتزامن:
Synchronized (كائن) {// عبارات ليتم مزامنتها}
حيث ، الكائن هو إشارة إلى الكائن المتزامن. إذا كان كل ما تريد مزامنته هو مجرد بيان ، فلا داعي للدعم المجعد. تضمن كتلة التزامن أن الدعوة إلى طريقة عضو الكائن تحدث فقط بعد إدخال مؤشر الترابط الحالي بنجاح إلى خط أنابيب الكائن.
فيما يلي نسخة معدلة من البرنامج السابق ، ويتم استخدام كتلة التزامن في طريقة Run ():
// يستخدم هذا البرنامج كتلة متزامنة. Out.println ("المقاطعات") ؛ ؛ ؛ ") ؛ المتصل OB3 = المتصل الجديد (الهدف ،" العالم ") ؛ // انتظر المواضيع لإنهاء محاولة {ob1.t.join () ؛ ob2.t.join () ؛ ob3.t.join () ؛} catch (interruptedException e) {system.out.println ("Interrupted") ؛
هنا ، لا يتم تعديل طريقة Call () عن طريق المزامنة. يتم الإعلان عن مزامنة في طريقة Run () لفئة المتصل. هذا يعطي نفس النتيجة الصحيحة في المثال أعلاه ، لأن كل مؤشر ترابط ينتظر نهاية الخيط السابق قبل تشغيله.
Java inter-thread Communication <br /> يستبدل MultiThreading برامج حلقة الأحداث عن طريق تقسيم المهام إلى وحدات منفصلة ومنطقية. المواضيع لها ميزة ثانية: يبقى بعيدا عن الاقتراع. عادة ما يتم تحقيق الاقتراع عن طريق حلقة تكرر ظروف المراقبة. بمجرد إنشاء الشروط ، يجب اتخاذ إجراءات مناسبة. هذا يضيع وقت وحدة المعالجة المركزية. على سبيل المثال ، ضع في اعتبارك مشكلة التسلسل الكلاسيكية عندما يقوم مؤشر ترابط بإنشاء بيانات بينما يستهلك برنامج آخر. لجعل المشكلة أكثر إثارة للاهتمام ، افترض أن مولد البيانات يجب أن ينتظر حتى ينهي المستهلك العمل قبل أن يتمكن من إنشاء بيانات جديدة. في نظام الاقتراع ، يضيع المستهلكون الكثير من دورات وحدة المعالجة المركزية أثناء انتظار المنتج لإنشاء البيانات. بمجرد الانتهاء من المنتج العمل ، سيبدأ في الاقتراع ، مما يضيع المزيد من وقت وحدة المعالجة المركزية في انتظار انتهاء عمل المستهلك ، وهكذا. من الواضح أن هذا الموقف ليس شائعًا.
لتجنب الاقتراع ، تتضمن Java آلية اتصال بين العمليات التي يتم تنفيذها من خلال WAIT () ، وإخطار () وإخطار () أساليب. يتم تنفيذ هذه الطرق في الكائنات ذات الأساليب النهائية ، لذلك تحتوي جميع الفئات عليها. لا يمكن استدعاء هذه الطرق الثلاثة إلا في الطريقة المتزامنة. على الرغم من أن هذه الأساليب متقدمة للغاية من خلال منظور رؤية علوم الكمبيوتر ، إلا أنها بسيطة للغاية في الممارسة العملية:
انتظر () يخبر الخيط المدعو للتخلي عن الأنبوب والذهاب إلى النوم حتى تدخل الخيوط الأخرى نفس الأنبوب واتصل الإخطار ().
تقوم إخطار () بإعادة ترابط الخيط الأول في نفس الكائن لاستدعاء Wait ().
تقوم الإخطار () بإعادة جميع مؤشرات الترابط في نفس الكائن الذي يستدعي Wait (). الخيط مع أعلى الأولوية يتم تشغيله أولاً.
يتم الإعلان عن هذه الطرق في الكائن على النحو التالي:
Final Void Wait () رميات InterruptedException الفراغ النهائي الإخطار () الفراغ النهائي الإخطار ()
يوجد شكل آخر من أشكال الانتظار () يتيح لك تحديد وقت الانتظار.
أخطاء برنامج المثال التالي تنفذ مشكلة منتج/مستهلك بسيط. يتكون من أربع فئات: تمكنت من الحصول على تسلسل متزامن ؛
// تنفيذ غير صحيح للمنتج والمستهلك Q {int n ؛ هذا .n = n ؛ ) .start () ؛ = Q ؛ ) {Q = New Q () ؛
على الرغم من أن أساليب PUT () و GET () في فئة Q متزامنة ، لا شيء يمنع المنتج من تجاوز المستهلك ، ولا شيء يمنع المستهلك من استهلاك نفس التسلسل مرتين. وبهذه الطريقة تحصل على إخراج الخطأ التالي (سيتغير الإخراج بسرعة المعالج والمهام المحملة):
وضع: 1got: 1got: 1got: 1got: 1got: 1put: 2put: 3put: 4put: 5put: 7got: 7
بعد أن يولد المنتج 1 ، يحصل المستهلك على نفس 15 مرة بدورها. يواصل المنتجون توليد 2 إلى 7 ، وليس للمستهلكين فرصة للحصول عليها.
لكتابة هذا البرنامج بشكل صحيح في Java ، استخدم Wait () وإخطار () لتمييز كلا الاتجاهين ، كما هو موضح أدناه:
// تنفيذ صحيح للمنتج والمستهلك Q {int n ؛ . Wait () ؛ ). بينما (صواب) {Q.Put (i ++) ؛ public void run () {بينما true) {q.get () ؛ (س)
GET INTROTION () ، WAIT () يسمى. هذا يعلق التنفيذ حتى يقوم المنتج بإبلاغ البيانات بأنها جاهزة. في هذا الوقت ، يتم استئناف GET () الداخلية (). بعد الحصول على البيانات ، احصل على () مكالمات الإخطار (). هذا يخبر المنتج أنه يمكنك إدخال المزيد من البيانات في التسلسل. في PUT () ، انتظر () تعليق التنفيذ حتى يأخذ المستهلك العنصر في التسلسل. عند استمرار التنفيذ ، يتم وضع عنصر البيانات التالي في التسلسل ويتم استدعاء الإخطار () ، والذي يخطر المستهلك أنه يجب إزالة البيانات.
فيما يلي إخراج البرنامج ، والذي يوضح بوضوح سلوك التزامن:
وضع: 1got: 1put: 2got: 2put: 3got: 3put: 4got: 4put: 5got: 5