نمط المستهلك المنتج هو النمط الأكثر شيوعًا بين متعدد الخيوط: يولد خيط المنتج (واحد أو أكثر) الخبز ويضعه في السلة (مجموعة أو صفيف) ، وفي نفس الوقت ، يخرج خيط المستهلك (واحد أو أكثر) الخبز من السلة (مجموعة أو صفيف) ويستهلكه. على الرغم من أن لديهم مهام مختلفة ، فإن الموارد التي يعالجونها هي نفسها ، والتي تعكس طريقة التواصل بين الخيوط.
ستشرح هذه المقالة أولاً وضع المنتجين الفرديين والمستهلكين الفرديين ، ثم يشرحون وضع نموذج متعدد المنتجات ومستهلكين. سيتم أيضًا تنفيذ هذين الوضعين باستخدام آلية Wait ()/nofity ()/nofityall () وآلية القفل ()/inlock () على التوالي.
قبل البدء في إدخال النمط ، اشرح تفاصيل استخدام WAIT () ، وإخطار () وإخطار () الأساليب وكذلك الاستخدام المحسن للقفل ()/فتح () ، وينتظر ()/إشارة ()/signalall ().
1. مبدأ الانتظار والاستيقاظ
انتظر () ، والإخطار () وإخطار () على التوالي تمثل الخيوط التي تدخل النوم ، وتستيقظ خيط النوم ، واستيقظ على جميع خيوط النوم. ولكن ، أي موضوع هو الكائن؟ بالإضافة إلى ذلك ، يجب استخدام جميع الطرق الثلاثة الموضحة في وثائق API تحت فرضية شاشة صالحة (والتي يمكن فهمها على أنها تحمل قفل). ما علاقة هذه الطرق الثلاثة بالقفل؟
يمكن استخدام كتلة رمز التزامن المزامنة (OBJ) {} أو وظائف المزامنة كمثال ، وانتظار () ، وإخطار () ، وإخطار () في بنية الكود الخاصة بهم لأنها تحتوي جميعها على أقفال.
بالنسبة للكتلتين التاليتين لعمليات التزامن ، يتم استخدام Lock OBJ1 و LOCK OBJ2 على التوالي. قم بتنفيذ مؤشر الترابط 1 والموضوع 2 ، قم بتنفيذ رمز التزامن المقابل لـ OBJ1 ، والمعلومات 3 وخيط 4 تنفيذ رمز التزامن المقابل لـ OBJ2.
الفئة mylock تنفذ runnable {public int flag = 0 ؛ كائن OBJ1 = كائن جديد () ؛ كائن OBJ2 = كائن جديد () ؛ public void run () {بينما (true) {if (flag ٪ 2 = 0) {synchronized (obj1) {// threads t1 و t2 تنفيذ مهمة المزامنة هذه // try {obj1.wait () ؛ Synchronized (obj2) {// thread t3 و t4 تنفيذ مهمة المزامنة هذه // حاول {obj2.wait () ؛ جديد mylock () ؛ الموضوع T1 = مؤشر ترابط جديد (مل) ؛ الموضوع T2 = مؤشر ترابط جديد (مل) ؛ الموضوع T3 = مؤشر ترابط جديد (مل) ؛ الموضوع T4 = مؤشر ترابط جديد (مل) ؛ t1.start () ؛ t2.start () ؛ حاول {thread.sleep (1)} catch (interruptedException I) {} ؛ ml.flag ++ ؛ t3.start () ؛ t4.start () ؛ }}عندما يبدأ T1 في التنفيذ إلى الانتظار () ، فإنه سيدخل حالة النوم ، ولكنه ليس نومًا طبيعيًا ، ولكن ينام في تجمع خيوط تم تحديده بواسطة OBJ1 (في الواقع يتوافق الشاشة مع تجمع الخيوط ، ولكن يتم ربط الشاشة والقفل معًا في هذا الوقت). عندما يبدأ T2 في التنفيذ ، يجد أن Lock OBJ1 يحتفظ به مؤشرات الترابط الأخرى وسيدخل حالة النوم. هذه المرة لأن مورد القفل ينتظر بدلاً من النوم الذي تم إدخاله بواسطة Wait (). نظرًا لأن T2 قد قرر بالفعل أنه يتقدم بطلب للحصول على قفل OBJ1 ، فإنه سيدخل أيضًا نوم بركة مؤشر ترابط OBJ1 ، بدلاً من النوم العادي. وبالمثل ، فإن T3 و T4 ، سيدخل هذان الخيطان تجمع مؤشر ترابط OBJ2 للنوم.
عندما يتم تنفيذ مؤشر ترابط لإخطار () ، فإن هذا الإخطار () سوف يستيقظ بشكل عشوائي من أي مؤشر ترابط في تجمع مؤشرات الترابط المقابل لقفله. على سبيل المثال ، سوف يستيقظ OBJ1.NOTIFY () أي موضوع نائم في تجمع الخيط OBJ1 (بالطبع ، إذا لم يكن هناك خيط نائم ، فلن يفعل شيئًا). وبالمثل ، استيقظ إخطار () جميع خيوط النوم في مجموعة الخيوط المقابلة للقفل.
ما يجب عليك اكتشافه هو "القفل المقابل" ، لأنه يجب تحديد القفل بشكل صريح عند استدعاء Wait () ، وإخطار () وإخطار (). على سبيل المثال ، OBJ1.WAIT (). إذا تم حذف القفل إليه ، فهذا يعني هذا الكائن ، أي أن بادئ هذه الطرق الثلاثة لا يمكن حذفها إلا في وظائف التزامن غير المنتظمة.
باختصار ، عند استخدام التزامن ، يتم استخدام القفل ، ويحتوي الخيط على منزل ، ويتم تحديد كل أساسه بواسطة قفل الانتماء. على سبيل المثال ، عند مزامنة مؤشر الترابط ، فإنه يحدد ما إذا كان القفل خاملاً لتقرير ما إذا كان سيتم تنفيذ الكود اللاحق ، ويحدد أيضًا ما إذا كان سيتم الذهاب إلى تجمع مؤشر ترابط معين للنوم. عند الصحوة ، سوف يستيقظ فقط الخيط في تجمع الخيوط المقابل للقفل.
في تطبيق هذه الأساليب ، بشكل عام في مهمة ، وانتظر () وإخطار ()/إخطار () تظهر في أزواج وتنفيذ واحدة تلو الأخرى. بمعنى آخر ، خلال هذه الجولة من التنفيذ المتزامن الذري ، يتم تنفيذ إما WAIT () للنوم ، أو يتم تنفيذ الإخطار () لإيقاظ مؤشر ترابط النوم في تجمع الخيوط. لتحقيق تنفيذ انتقائي ، يمكنك التفكير في استخدام العلامات كأساس للحكم. الرجوع إلى الأمثلة التالية.
2.lock والحالة
الطرق الثلاث لسلسلة Wait () محدودة للغاية لأن إجراءات النوم والاستيقاظ مقترنة تمامًا بالقفل. على سبيل المثال ، يمكن للمعلومات المرتبطة بقفل OBJ1 أن يستيقظ فقط الخيط في تجمع الخيط OBJ1 ، ولكن لا يمكن أن يوقظ الخيط المرتبط بقفل OBJ2 ؛ على سبيل المثال ، عندما تم مزامنة المزامنة المتزامنة في الأصل ، تم الحصول على القفل بشكل تلقائي بشكل ضمني عند بدء التزامن ، وبعد تنفيذ المهمة بأكملها ، تم إطلاقها ضمنيًا القفل تلقائيًا ، مما يعني أن إجراء الحصول على القفل وإطلاق القفل لا يمكن التحكم فيه يدويًا.
بدءًا من JDK 1.5 ، توفر Java حزمة java.util.concurrent.locks ، والتي توفر واجهة القفل وواجهة الشرط وواجهة ReadWritelock. أول واجهتين تفصل طرق القفل والمراقبة (النوم ، عمليات الاستيقاظ). توفر واجهة القفل فقط الأقفال. من خلال طريقة القفل newconditon () ، يمكن إنشاء شاشات واحدة أو أكثر المرتبطة بالقفل. كل شاشة لها أساليب النوم والاستيقاظ. بمعنى آخر ، يستبدل Lock استخدام الأساليب المتزامنة وكتل التعليمات البرمجية المتزامنة ، ويستبدل الشرط استخدام طرق شاشة الكائن.
كما هو مبين في الشكل أدناه:
عندما ينفذ مؤشر ترابط الشرط 1.await () ، سيقوم مؤشر الترابط بإدخال تجمع مؤشر الترابط المقابل لجهاز الشاشة 1 للنوم. عند تنفيذ الحالة 1.Signal () ، سيتم إيقاظ أي مؤشر ترابط في مجموعة مؤشرات الترابط inction1 بشكل عشوائي. عندما يتم تنفيذ الحالة 1.Signalall () ، سيتم إيقاظ جميع المواضيع في مجموعة مؤشرات الترابط 1. وبالمثل ، فإن الشيء نفسه ينطبق على مراقبة الحالة 2.
حتى إذا كان هناك شاشات متعددة ، طالما ترتبط بكائن القفل نفسه ، يمكن تشغيل مؤشر الترابط الآخر عبر الشاشة. على سبيل المثال ، يمكن لخيط في حالة 1 تنفيذ الحالة.
لاستخدام طريقة ارتباط الأقفال والشاشات ، راجع الخطوات التالية:
استيراد java.util.concurrent.locks.*؛ قفل l = جديد reentrantlock () ؛ الشرط con1 = l.newcondition () ؛ الشرط con2 = l.newcondition () ؛ l.lock () ؛ جرب {// رمز القطاع الذي يحتوي على () ، إشارة () أو signalall () ...} أخيرًا {l.unlock () ؛ // نظرًا لأن قطاع الكود قد يكون غير طبيعي ، يجب تنفيذ فتح () ، يجب استخدام المحاولة ويجب إلغاء القفل () في الجزء الأخير}}للاستخدام المحدد ، يرجى الاطلاع على رمز المثال للقفل والشرط لاحقًا.
3. نموذج واحد للمنتج واحد المستهلك
مؤشر ترابط المنتج ، مؤشر ترابط المستهلك. لكل خبز ينتجه المنتج ، ضعه على اللوحة ، ويخرج المستهلك الخبز من اللوحة للاستهلاك. أساس المنتجين للحكم على ما إذا كان سيستمر الإنتاج هو أنه لا يوجد خبز على اللوحة ، في حين أن أساس المستهلكين للحكم على ما إذا كان يجب أن يستهلكوا أن هناك خبزًا على اللوحة. نظرًا لأنه في هذا الوضع ، يتم وضع رغيف واحد فقط من الخبز على اللوحة ، ويمكن حذف اللوحة ويمكن أن يقوم المنتج والمستهلك بتسليم الخبز خطوة بخطوة.
أولاً ، نحتاج إلى وصف هذه الفئات الثلاث: الأول هو المورد الذي تديره مؤشرات ترابط متعددة (هنا خبز) ، والثاني هو المنتج ، والثالث هو المستهلك. في المثال التالي ، أقوم بتغليف طرق إنتاج الخبز واستهلاك الخبز في فئات المنتج والمستهلك على التوالي ، وهو أمر أسهل في فهمه إذا كانت مغلفة في فئة الخبز.
// الوصف المورد: اسم وعدد الخبز ، يحدده عدد خبز فئة الخبز {اسم السلسلة العامة ؛ عدد int العام = 1 ؛ علم منطقي العام = خطأ ؛ // توفر هذه العلامة علامات الحكم لـ WAIT () وإخطار ()} // مورد الخبز الذي يعالجه المنتج والمستهلك هي نفسها. لضمان ذلك ، يمكن تصميم فئة الخبز وفقًا لنمط Singleton ، أو يمكن تمرير نفس كائن الخبز إلى المنتج والمستهلك من خلال طريقة البناء. يتم استخدام الطريقة الأخيرة هنا. // وصف منتج فئة المنتجين ينفذ Runnable {private Bread B ؛ // عضو المنتج: المورد الذي يريد معالجة المنتج (الخبز ب) {this.b = b ؛ } // توفير طريقة لإنتاج Bread Public Void Produce (اسم السلسلة) {B.Name = name + B.Count ؛ B.Count ++ ؛ } public void run () {بينما (صحيح) {synchronized (bread.class) {// استخدم bread.class كمعرف قفل بحيث يمكن أن تكون الكتل المزامنة من المنتجين والمستهلكين أن يستخدموا نفس القفل ، ولكن سيكون هناك (B. حاول {bread.class.wait () ؛} catch (interruptedException I) {}} Produce ("Bread") ؛ System.out.println (thread.currentThread (). --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- // يجب أيضًا مزامنة الإخطار () ، وإلا فإن القفل قد تم إصداره ، ولا يمكن تنفيذ إجراء الاستيقاظ // this.b = b ؛ system.out.println (thread.currentTherAd (). ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 2يجب إنتاج نتيجة التنفيذ النهائية واستهلاكها ، وهذه دورة مستمرة. على النحو التالي:
Thread-0 --- منتج ---- Bread1thread-1 --- المستهلك ------- Bread1Thread-0 --- منتج ---- Bread2Thread-1 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4. استخدم القفل والشرط لتحقيق نموذج الإنتاج والاستهلاك الفردي
الرمز كما يلي:
استيراد java.util.concurrent.locks.*؛ فئة الخبز {اسم السلسلة العامة ؛ عدد int العام = 1 ؛ علم منطقي العام = خطأ ؛ // توفير نفس كائن القفل وكائن الشرط نفسه للمنتجين والمستهلكين قفل القفل الثابت العام = جديد reentrantlock () ؛ حالة الحالة الثابتة العامة = lock.newcondition () ؛} ينفذ منتج الفئة Runnable {private bread b ؛ المنتج (الخبز ب) {this.b = b ؛ } Public Void Produce (اسم السلسلة) {B.Name = name + B.Count ؛ B.Count ++ ؛ } public void run () {بينما (صحيح) {// استخدم bread.lock لقفل خبز المورد .lock.lock () ؛ حاول {if (b.flag) {try {bread.condition.await () ؛} catch (interruptedException I) {}} produce ("bread") ؛ system.out.println (thread.currentThread (). ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bread.condition.signal () ؛ if (! system.out.println (thread.currentThread (). ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 25. نموذج الإنتاج والاستهلاك متعدد (خبز واحد)
نوضح هنا أولاً نموذج العديد من المنتجين والمستهلكين المتعددين ، ولكن على الأكثر في نفس الوقت. قد لا يكون هذا النموذج مثاليًا في الواقع ، ولكن من أجل أن يؤدي إلى النموذج الحقيقي متعدد الإنتاج ونموذج الاستهلاك المتعدد لاحقًا ، أعتقد أنه من الضروري شرح هذا النموذج هنا وتحليل هذا النموذج وكيف تطورت من الإنتاج الفردي والرمز الاستهلاك الفردي.
كما هو مبين في الشكل أدناه:
من الإنتاج الفردي والاستهلاك الفردي إلى الإنتاج المتعدد والاستهلاك المتعدد ، بسبب مشكلات السلامة متعددة الخيوط وقضايا Deadlock ، هناك مشكلتان يجب مراعاتهما:
بالنسبة لطرف واحد ، كيف يمكن أن تحقق متعددة الخيوط نفس القدرة على الإنتاج أو الاستهلاك مثل التقدير الفردي؟ وبعبارة أخرى ، كيفية جعل متعدد الخيوط تبدو واحدة. الفرق الأكبر بين متعددة الخيوط والخيوط الفردية هو قضايا السلامة متعددة الخيوط. لذلك ، طالما أنك تضمن أن المهام التي تنفذها متعدد الخيوط يمكن مزامنة.
يسأل السؤال الأول عن مشكلة متعدد الخيوط في طرف واحد ، والسؤال الثاني ينظر في كيف يمكن للطرفان التعاون بشكل متناغم لإكمال الإنتاج والاستهلاك. أي كيفية ضمان نوم جانب واحد من المنتج والمستهلك بينما يكون الجانب الآخر نشطًا. فقط استيقظ على الطرف الآخر عندما ينتهي أحد الطرفين من أداء مهمة التزامن.
في الواقع ، من موضوع واحد إلى سلسلة متعددة ، هناك مشكلتان يجب النظر فيهما: خارج التزامن ودويك. (1) عندما يكون لدى كل من المنتج وجانب المستهلك قوائم متعددة ، يمكن اعتبار الروابط المتعددة للمنتج خيطًا ككل ، والروابط المتعددة لجانب المستهلك أيضًا ككل ، وهو يحل مشكلة سلامة الخيط. (2) يعتبر الجمع بين الإنتاج بأكمله ويعتبر المستهلك بأكمله متعدد الخيوط لحل مشكلة الجمود. الطريقة لحل الجمود في جافا هي استيقاظ الطرف الآخر أو استيقاظ كل شيء.
والسؤال هو كيفية ضمان التزامن بين مؤشرات ترابط متعددة لطرف معين؟ يتم تحليل رمز المستهلك الواحد عن طريق التنفيذ متعدد الخيوط كمثال.
بينما (صواب) {synchronized (bread.class) {if (! system.out.println (thread.currentThread (). -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------لنفترض أن موضوع الاستهلاك 1 يستيقظ سلسلة الاستهلاك 2 بعد استهلاك رغيف الخبز ، ويستمر في الحلقة ، والحكم على (! العلم) ، سوف ينتظر ، ويتم إصدار القفل. على افتراض أن وحدة المعالجة المركزية تختار فقط مؤشر ترابط المستهلك 2 ، فإن مؤشر ترابط المستهلك 2 سوف يدخل الانتظار. عندما ينتج المنتج رغيف الخبز ، لنفترض أن خيط الاستهلاك 1 يستيقظ ، فسوف يستمر في استهلاك الخبز الذي تم إنتاجه حديثًا من بيان الانتظار ، لنفترض أن موضوع الاستهلاك 2 يستيقظ مرة أخرى. عندما يتم تحديد مؤشر ترابط الاستهلاك 2 بواسطة وحدة المعالجة المركزية ، سيستهلك مؤشر ترابط الاستهلاك 2 أيضًا لأسفل من عبارة الانتظار ، ويتم استهلاك الخبز الذي تم إنتاجه للتو. تنشأ المشكلة مرة أخرى. تستهلك خيوط الاستهلاك المستمرة 1 و 2 نفس الخبز ، مما يعني أن الخبز مستهلك مرارًا وتكرارًا. هذه مشكلة أخرى متعددة الخيوط.
بعد الحديث عنها لفترة طويلة ، من السهل جدًا التحليل بعد توسيع خط البصر. طالما أن الخيطين أو أكثر من طرف واحد ينتظر الحكم B.Flag ، فقد يتم إيقاظ المواضيع أو أكثر بشكل مستمر واستمرار إنتاجها أو استهلاكها. هذا يخلق مشكلة التزامن متعدد الخيوط.
تكمن مشكلة انعدام الأمن في حقيقة أن خيوط متعددة على نفس الطرف تواصل إنتاجها أو تستهلكها لأسفل بعد الصحوة المستمرة. هذا ناتج عن البيان if. إذا تمكن مؤشر ترابط الانتظار من العودة لتحديد ما إذا كان B.FLAG صحيحًا بعد الاستيقاظ ، فيمكنه أن يجعله يقرر ما إذا كان يجب متابعة الانتظار أو الإنتاج أو الاستهلاك لأسفل.
يمكنك استبدال البيان IF ببيان من الوقت لتلبية المتطلبات. وبهذه الطريقة ، بغض النظر عما إذا كانت خيوط متعددة على طرف معين تستيقظ باستمرار ، فسوف يعودون إلى القاضي B.Flag.
بينما (صحيح) {synchronized (bread.class) {بينما (! system.out.println (thread.currentThread (). -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------تم حل أول مشكلة سلامة متعددة مؤشرات الترابط ، ولكن حدثت مشاكل ديدان. هذا سهل التحليل. يعتبر المنتج ككل والمستهلك أيضًا. عندما تنتظر جميع خيوط المنتج (يتم إيقاظ خيوط حزب الإنتاج بشكل مستمر ، ستنتظر جميع خيوط الحزب) ، وسيظهر المستهلك أيضًا ، وسيظهر Deadlock. في الواقع ، إذا نظرت إليها بطريقة تضخيم ، يعتبر المنتج والمستهلك موضوعًا واحدًا على التوالي. هذين الخيطين يشكلان خيوط متعددة. عندما ينتظر أحد الجانبين ولا يمكن أن يوقظ الجانب الآخر ، فإن الجانب الآخر سينتظر بالتأكيد ، لذلك سيكون مسدود.
بالنسبة لمشكلة الجمود بين الطرفين ، طالما أنك تضمن أنه يمكن إيقاظ الطرف الآخر ، بدلاً من الصحوة المستمرة للطرف ، يمكن حله. ما عليك سوى استخدام الإخطار () أو SignalAll () ، أو يمكنك إيقاظ مؤشر الترابط الآخر من خلال الإشارة () لحل المشكلة. انظر الرمز الثاني أدناه.
وفقًا للتحليل أعلاه ، إذا تم تحسين رمز الإنتاج الفردي ونموذج الاستهلاك الفردي ، فيمكن تغييره إلى نموذج خبز فردي متعدد الإنتاج ومتعدد الاستهلاك.
// Code Segment 1class Bread {public string name ؛ عدد int العام = 1 ؛ علم منطقي العام = خطأ ؛ } // وصف منتج فئة المنتجين الذي يقوم بتشغيله {private bread b ؛ المنتج (الخبز ب) {this.b = b ؛ } Public Void Produce (اسم السلسلة) {B.Name = name + B.Count ؛ B.Count ++ ؛ } public void run () {بينما (صحيح) {synchronized (bread.class) {بينما (b.flag) {try {bread.class.wait () ؛} catch (interruptedException I) {}} produce ("bread") ؛ System.out.println (thread.currentTherAd (). ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- استهلاك السلسلة () {return B.Name ؛ system.out.println (thread.currentThread (). ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ } فئة Productsume_5 Thread Con_t1 (Con) ؛فيما يلي تم إعادة تمثيل الكود باستخدام القفل والكونديتون ، باستخدام Signal () لإيقاظ الخيط الآخر.
// Code Segment 2Import java.util.concurrent.locks.*؛ class bread {public string name ؛ عدد int العام = 1 ؛ علم منطقي العام = خطأ ؛ قفل قفل ثابت عام = جديد reentrantlock () ؛ حالة ثابتة عامة pro_con = lock.newcondition () ؛ الحالة الثابتة العامة con_con = lock.newcondition () ؛} // وصف منتج فئة المنتجين الذي يديره {private bread b ؛ المنتج (الخبز ب) {this.b = b ؛ } Public Void Produce (اسم السلسلة) {B.Name = name + B.Count ؛ B.Count ++ ؛ } public void run () {بينما (صحيح) {bread.lock.lock () ؛ جرب {بينما (b.flag) {try {bread.pro_con.await () ؛} catch (interruptedException I) {}} produce ("bread") ؛ system.out.println (thread.currentThread (). ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bread.con.signal () بينما (! system.out.println (thread.currentThread (). ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- //2. Create producer and consumer objects Producer pro = new Producer(b); Consumer con = new Consumer(b); //3. Create thread object Thread pro_t1 = new Thread(pro); Thread pro_t2 = new Thread(pro); Thread con_t1 = new Thread(con); Thread con_t2 = new Thread(con); pro_t1.start(); pro_t2.start(); con_t1.start(); con_t2.start () ؛دعنا نلخص قضايا المزيد من الإنتاج والمزيد من الاستهلاك:
(1). إن حل التزامن متعدد الخيوط من طرف معين هو الاستخدام أثناء (العلم) لتحديد ما إذا كان الانتظار ؛
(2). إن حل مشكلة الجمود لكلا الطرفين هو إيقاظ الطرف الآخر. يمكنك استخدام طريقة الإخطار () أو SignalAll () أو إشارة () من شاشة الطرف الآخر.
6. المزيد من نماذج الإنتاج والاستهلاك
هناك عدة مؤشرات ترابط المنتج ومواضيع المستهلك المتعددة. يضع المنتج الخبز المنتجة في سلة (مجموعة أو صفيف) ، ويأخذ المستهلك الخبز من السلة. أساس المنتجين للحكم على الإنتاج المستمر هو أن السلة ممتلئة ، والأساس للمستهلكين للحكم على استمرار الاستهلاك هو ما إذا كانت السلة فارغة. بالإضافة إلى ذلك ، عندما يخرج المستهلك الخبز ، يصبح الموضع المقابل فارغًا مرة أخرى ، ويمكن للمنتج العودة إلى الوراء ومواصلة الإنتاج من موضع بداية السلة ، والذي يمكن تحقيقه عن طريق إعادة تعيين مؤشر السلة.
في هذا النموذج ، بالإضافة إلى وصف المنتجين والمستهلكين والخبز ، من الضروري أيضًا وصف حاوية السلة. لنفترض أنه يتم استخدام صفيف كحاوية ، في كل مرة ينتج فيها المنتج واحدًا ، يتحول مؤشر الإنتاج للخلف ، وفي كل مرة يستهلك المستهلك واحدة ، يتحول مؤشر الاستهلاك للخلف.
الرمز كما يلي: يمكنك الرجوع إلى رمز المثال الوارد في فئة الشرط API->
استيراد java.util.concurrent.locks.*؛ سلة الفئة {خبز خاص [] arr ؛ // حجم سلة السلة (حجم int) {arr = new Bread [size] ؛ } // مؤشر داخل وخارج private int in_ptr ، out_ptr ؛ // كم عدد الخبز المتبقي في سلة خاصة int اليسار ؛ قفل قفل خاص = جديد reentrantlock () ؛ حالة خاصة كاملة = lock.newcondition () ؛ حالة خاصة فارغة = lock.newcondition () ؛ // الخبز في سلة public void in () {lock.lock () ؛ حاول {بينما (left == arr.length) {try {full.await () ؛} catch (interruptedException I) { system.out.println ("put the bread:"+arr [in_ptr] .getName ()+"------- in basket ["+in_ptr+"]") ؛ اليسار ++ ؛ if (++ in_ptr == arr.length) {in_ptr = 0 ؛} فارغة. } أخيرًا {lock.unlock () ؛ }} // الخبز من Basket Public Bread Out () {lock.lock () ؛ جرب {بينما (left == 0) {try {fmare.await () ؛} catch (interruptedException I) { System.out.println ("Get the Bread:"+Out_Bread.getName ()+"---------- من السلة ["+Out_ptr+"]") ؛ غادر--؛ if (++ out_ptr == arr.length) {out_ptr = 0 ؛} full.signal () ؛ العودة out_bread ؛ } أخيرًا {lock.unlock () ؛ }}} class bread {private string name ؛ الخبز (اسم السلسلة ، int num) {this.name = name + num ؛ } السلسلة العامة getName () {return this.name ؛ }} يقوم منتج الفئة بتنفيذ Runnable {Private Basket Basket ؛ ثابت عام int num = 1 ؛ // الرقم الأول لمنتج اسم الخبز (سلة ب) {this.basket = b ؛ } public void run () {بينما (صحيح) {basket.in () ؛ جرب {thread.sleep (10) ؛} catch (interruptedException I) {}}}} Class Consumers Runnable {Private Basket Basket ؛ الخبز الخاص i_get ؛ المستهلك (سلة ب) {this.basket = b ؛ } public void run () {when (true) {i_get = scatear.out () ؛ جرب {thread.sleep (10) ؛} catch (interruptedException I) {}}}} الفئة العامة produceconsume_7 {public static void main (string [] args) {basket b = new basket (20) ؛ // حجم السلة = 20 Producer Pro = New Producer (B) ؛ المستهلك con = مستهلك جديد (ب) ؛ Thread pro_t1 = مؤشر ترابط جديد (Pro) ؛ Thread pro_t2 = مؤشر ترابط جديد (Pro) ؛ الموضوع con_t1 = مؤشر ترابط جديد (con) ؛ الموضوع con_t2 = مؤشر ترابط جديد (con) ؛ الموضوع con_t3 = مؤشر ترابط جديد (con) ؛ pro_t1.start () ؛ pro_t2.start () ؛ con_t1.start () ؛ con_t2.start () ؛ con_t3.start () ؛ }}يتضمن ذلك المستهلكين والمنتجين والخبز والسلال ، حيث يكون الخبز والسلال موارد تديرها خيوط متعددة. ينتج خيط المنتج الخبز ويضعه في السلة ، ويخرج خيط المستهلك الخبز من السلة. الكود المثالي هو تغليف كل من مهام الإنتاج ومهام الاستهلاك في فئة الموارد. نظرًا لأن الخبز عنصر في حاوية السلة ، فهو غير مناسب للتعبئة في فئة الخبز ، والتعبئة في السلة تجعل من السهل تشغيل الحاوية.
لاحظ أنه يجب عليك وضع جميع الرموز التي تتضمن عمليات الموارد داخل القفل ، وإلا ستحدث مشكلة التزامن متعدد الخيوط. على سبيل المثال ، يتم تعريف الطريقة المنتجة للخبز في فئة المنتج ، ثم يتم استخدامها كمعلمة إلى سلة الطريقة. في السلة ، على سبيل المثال ، سلة.
تستند المقالة أعلاه إلى منتج Java ونموذج المستهلك (تحليل مفصل) وهو المحتوى بالكامل الذي يشاركه المحرر. آمل أن يعطيك مرجعًا وآمل أن تتمكن من دعم wulin.com أكثر.