كلمة رئيسية متزامنة
يمكن للمفتاح المتزامن تعديل الوظائف والبيانات داخل الوظائف. سواء تم إضافته إلى الأساليب أو الكائنات ، فإن القفل الذي يكتسبه هو كائن ، بدلاً من معالجة قطعة من التعليمات البرمجية أو الوظيفة كقفل.
1. عندما يصل اثنين من مؤشرات الترابط المتزامنة إلى كتلة الكود المتزامن (هذا) في نفس الكائن ، يمكن تنفيذ مؤشر ترابط واحد فقط لفترة من الزمن ، ويمكن للموضوع الآخر تنفيذ هذا الرمز فقط بعد تنفيذ مؤشر الترابط الحالي.
2. عندما يصل مؤشر ترابط إلى كتلة رمز متزامنة (هذا) متزامن في كائن ، لا يزال بإمكان مؤشرات الترابط الأخرى الوصول إلى كتل رمز أخرى غير متزامنة (هذا) في هذا الكائن.
3. تجدر الإشارة هنا إلى أنه عندما يصل مؤشر ترابط إلى كتلة رمز متزامنة (هذا) من كائن ، سيتم حظر مؤشرات الترابط الأخرى من الوصول إلى كتل رمز متزامنة أخرى (هذا) في هذا الكائن.
4. ما ورد أعلاه ينطبق أيضًا على كتل رمز التزامن الأخرى ، أي عندما يصل مؤشر ترابط إلى كتلة رمز المزامنة (هذا) متزامن للكائن ، يحصل مؤشر الترابط على قفل كائن الكائن. علاوة على ذلك ، فإن كل كائن (على سبيل المثال ، مثيل الفئة) يتوافق مع القفل. يتم حظره بمجرد تنفيذ الطريقة ، وسيتم احتلال القفل حصريًا حتى يتم إصداره عندما يعود من الطريقة ويعيد إدخال الحالة القابلة للتنفيذ. تضمن هذه الآلية أنه في الوقت نفسه ، لكل كائن ، على الأكثر واحدة من جميع وظائف الأعضاء المعلنة ، يكون المزامنة في حالة قابلة للتنفيذ (لأنه يمكن أن يكتسب مؤشر ترابط واحد في معظمه قفل الكائن) ، وبالتالي تجنب الوصول إلى متغيرات أعضاء الفصل الصراع .
عيوب الطريقة المتزامنة:
نظرًا لأن قفلات المزامنة ، فإن الكائن الذي يطلق على طريقة المزامنة هذه ، أي عندما يقوم مؤشر ترابط P1 بتنفيذ هذه الطريقة في مؤشرات ترابط مختلفة ، فإنها ستشكل استثناءات متبادلة ، وبالتالي تحقيق تأثير المزامنة. ولكن تجدر الإشارة هنا إلى أن كائنًا آخر من فئة من هذا الكائن يمكن أن يطلق على هذه الطريقة بشكل تعسفي مع إضافة الكلمة الرئيسية المتزامنة. إن جوهر المزامنة هو تطبيق مؤشرات الترابط التي حصلت على كائن P1 فقط. هذه الحالة. سنشرح هذا الموقف بالتفصيل أدناه:
أولاً ، دعنا نقدم كائنين مغلقين مع الكلمة الرئيسية المتزامنة: يمكن أن يضيف كائن وفئة - أقفال كائن أو أقفال فئة. من هذه الفئة ، لا يزال بإمكان كائنات أخرى من هذه الفئة استخدام الطريقة المتزامنة التي أغلقت الكائن السابق.
واحدة من القضايا الرئيسية التي نناقشها هنا هي: "هل ستسمي نفس الفصل والحالات المختلفة نفس الطريقة ، هل ستكون هناك مشكلة التزامن؟"
ترتبط مشكلة التزامن فقط بالمورد ، ويعتمد على ما إذا كان المورد ثابتًا. بالنسبة للبيانات الثابتة ، فإن وظيفتك نفسها تنتمي إلى مؤشرات ترابط مختلفة لقراءتها وكتابتها في نفس الوقت ، ولن تنشئ وحدة المعالجة المركزية أخطاء. أنت. حتى إذا كان لديك رمزان مختلفان يعملان في نوى مختلفة من وحدة المعالجة المركزية وكتابة عنوان ذاكرة في نفس الوقت ، فإن آلية ذاكرة التخزين المؤقت ستقفل واحدة في L2 أولاً. ثم قم بتحديثه ومشاركته مع قلب آخر ، ولن تكون هناك أخطاء ، وإلا فإن Intel أو AMD ستكون سدى.
لذلك ، طالما أنك لا تملك نفس المورد أو المتغير الذي يشاركان فيه كود ، فلن يكون هناك عدم تناسق للبيانات. علاوة على ذلك ، فإن المكالمات إلى كائنات مختلفة من نفس الفئة لها مداخن مختلفة تمامًا ، وهي غير ذات صلة تمامًا.
نستخدم هنا مثالًا لتوضيح عملية مبيعات التذاكر ، حيث تكون مواردنا المشتركة هي العدد المتبقي من التذاكر.
Package Com.Test ؛ يمتد Threadsafetest Presidents {private static int num = 1 ؛ باطلة (اسم السلسلة) {if (num> 0) {system. (أكمل في حوالي 5 ثوان). ؛ (النظام: عدد الأصوات: " + num) ؛ args []) {E.Printstacktrace () ؛ قم بتشغيل الرمز أعلاه والإخراج الذي نحصل عليه هو:
موصل التذاكر LI XX: عدد تذاكر الاختبار أكبر من 0 موصل التذاكر LI XX: يتم جمع الدفع (مكتمل في حوالي 5 ثوان). . . بائع التذاكر King X: عدد تذاكر الاختبار أكبر من 0 بائع التذاكر King X: يتم جمع الدفع (مكتمل في حوالي 5 ثوان). . . بائع التذاكر LI XX: طباعة الفاتورة ، نظام إكمال مبيعات التذاكر: العدد الحالي من الأصوات: 0 بائع التذاكر Wang X: طباعة الفاتورة ، نظام إكمال مبيعات التذاكر: العدد الحالي من الأصوات: -1 تحذير: عدد الأصوات أقل من 0 ، تظهر الأرقام السلبية
بناءً على نتائج الإخراج ، يمكننا أن نجد أن الأصوات المتبقية هي -1 ، وهناك مشكلة خطأ في التزامن. والسبب في ذلك هو أن كائدي المثيل الذي أنشأناه قد قاموا بتعديل المورد الثابت الثابت int num = 1 في نفس الوقت. ثم نقوم بإزالة المعدل الثابت في المربع في الكود أعلاه ، ثم ندير البرنامج للحصول على:
موصل التذاكر LI XX: عدد تذاكر الاختبار أكبر من 0 موصل التذاكر LI XX: يتم جمع الدفع (مكتمل في حوالي 5 ثوان). . . بائع التذاكر King X: عدد تذاكر الاختبار أكبر من 0 بائع التذاكر King X: يتم جمع الدفع (مكتمل في حوالي 5 ثوان). . . بائع التذاكر LI XX: طباعة الفاتورة ، ونظام إكمال مبيعات التذاكر: العدد الحالي من التذاكر: 0 بائع التذاكر Wang X: طباعة الفاتورة ، نظام إكمال مبيعات التذاكر: العدد الحالي من التذاكر: 0
بعد تعديل الدرجة ، يعمل البرنامج دون أي مشاكل. ولكن هذا يتعارض مع توقعاتنا بأن عدة مؤشرات ترابط يمكنها التعامل مع الموارد المشتركة في نفس الوقت (بعد ثابت ، يتغير NUM من الموارد المشتركة إلى متغيرات الأعضاء المملوكة لكل حالة) ، وهو ما لا نريده.
في الرموز المذكورة أعلاه ، فإن الشيء الرئيسي الذي يجب تبنيه هو قفل الكائن. للسبب الذي ذكرته من قبل ، عندما تعدل حالتان مختلفتان من الفئة نفس المورد المشترك ، فإن وحدة المعالجة المركزية ستتخلف عن منطق البرنامج. لذلك ، نحتاج إلى تغيير نطاق القفل. في نفس الوقت.
Package Com.Test ؛ يمتد Threadsafetest Presidents {private static int num = 1 ؛ STATIC VOID (اسم السلسلة) {if (num> 0) {system. جمع الدفع (حوالي 5 ثوانٍ). () ؛ . args args []) ) {E.PrintStacktrace () ؛ اجعل البرنامج على النحو الوارد أعلاه للحصول على نتيجة التشغيل:
موصل التذاكر LI XX: عدد تذاكر الاختبار أكبر من 0 موصل التذاكر LI XX: يتم جمع الدفع (مكتمل في حوالي 5 ثوان). . . بائع التذاكر LI XX: اطبع التذاكر ، ونظام إكمال مبيعات التذاكر: العدد الحالي من التذاكر: 0 بائع التذاكر Wang X: لا تذاكر ، توقف مبيعات التذاكر
تتم إضافة المعدل الثابت إلى طريقة Sell () ، بحيث يصبح كائن القفل فئة. هذا سيحصل على النتائج التي نريدها كما هو متوقع.
تلخيص:
1. هناك استخدامان للكلمات الرئيسية المتزامنة: طريقة متزامنة وكتلة متزامنة.
2. في Java ، ليس مجرد مثيل فئة ، ولكن يمكن أن يتوافق كل فئة أيضًا مع القفل.
1. لا يمكن أن يتم مورث الكلمة الرئيسية المتزامنة. على الرغم من أنه يمكن استخدام المزامنة لتحديد الطرق ، إلا أن المزامنة لا تنتمي إلى جزء من تعريف الطريقة ، لذلك لا يمكن ورث الكلمة الرئيسية المتزامنة. إذا كانت الطريقة في الفئة الأصل تستخدم الكلمة الرئيسية المتزامنة وتجاوز الفئة الفرعية هذه الطريقة بشكل افتراضي ، فإن هذه الطريقة في الفئة الفرعية غير متزامنة ، ويجب عرضها لإضافة الطريقة في الفئة الفرعية فقط. . بالطبع ، يمكنك أيضًا استدعاء الطرق المقابلة في الفئة الأصل في الفئة الفرعية. يجري مزامنة. يحب،
أضف الكلمة الرئيسية المتزامنة إلى الفئة الفرعية:
الفئة Parent {public synchronized void method () {}} class child يمتد Parent {public synchronized void method () {}} استدعاء طريقة الفئة الأصل:
Class Parent {public synchronized void method () {}} class child {public void method () {super.method () ؛ 2. لا يمكن استخدام الكلمة الرئيسية المتزامنة عند تحديد طريقة الواجهة.
3. لا يمكن للمُنشئ استخدام الكلمة الرئيسية المتزامنة ، ولكن يمكن استخدام الكتلة المتزامنة للمزامنة.
4. يمكن وضع الموضع المتزامن بحرية ، ولكن لا يمكن وضعه خلف نوع الإرجاع للطريقة.
5. لا يمكن استخدام الكلمة الرئيسية المتزامنة لمزامنة المتغيرات ، مثل الرمز التالي غير صحيح:
المزامنة العامة int n = 0 ؛
6. على الرغم من أن استخدام الكلمة الرئيسية المتزامنة هو طريقة التزامن الأكثر أمانًا ، إذا تم استخدامها بكميات كبيرة ، إلا أنها ستتسبب أيضًا في استهلاك الموارد غير الضروري وخسائر الأداء. على السطح ، يتم استخدام طريقة متزامنة ، ولكن في الواقع ، يتم استخدام الفئة. نفذت. الأساليب الثابتة تشبه الأساليب غير المنتظمة. ومع ذلك ، لن تؤثر الطرق الثابتة والطرق غير المنتظمة على بعضها البعض ، راجع الكود التالي:
الطبقة العامة MyTher1 يمتد سلسلة الفراغ الثابتة ؛ ") ؛} طريقة void المتزامنة العامة 2 () {method (" طريقة غير قاسية 2 ") ؛} طريقة الفراغ المتزامنة الثابتة العامة 3 () {method (" طريقة ثابتة 3 ") (طريقة ثابتة 4 ") ؛ ) استثناء {myther1 myther1 = new MyThread1 () ؛ ) ؛ النتيجة الجارية هي:
طريقة غير متتالية 1 طريقة ثابتة 3 طريقة
من نتائج التشغيل أعلاه ، يمكننا أن نرى أن Method2 و Method4 لن يتم تشغيلهما حتى يتم إكمال Method1 و Method3. لذلك ، يمكننا استنتاج أنه إذا استخدمنا متزامنًا لتحديد الطرق غير المنتظمة في الفصل ، فسيؤثر ذلك في هذه الفئة ، الطريقة الثابتة المحددة بواسطة المزامنة. هذا يشبه إلى حد ما قفل الجدول في جدول البيانات. لذلك ، فإن الاستخدام الكثيف لطريقة التزامن هذه سوف يقلل بشكل كبير من أداء البرنامج.
نصائح للوصول الأكثر أمانًا إلى الموارد المشتركة:
1. تحديد متغير المثيل الخاص بـ Private + GET Its ، بدلاً من تحديد متغير المثيل للجمهور/المحمي. إذا تم تعريف المتغير على أنه عام ، فيمكن للكائن الحصول عليه مباشرة تجاوز التحكم في طريقة التزامن في العالم الخارجي وتغييره. هذا هو أيضا واحد من التطبيقات القياسية لجافابين.
2. إذا كان متغير المثيل هو كائن ، مثل صفيف أو قائمة ArrayList ، فإن الطريقة أعلاه لا تزال غير آمنة ، لأنه عندما يحصل العالم الخارجي على كائن المثيل من خلال طريقة GET ويشير إلى كائن آخر ، ثم المتغير الخاص قد تغير أيضًا ، ألن يكون خطيرًا جدًا؟ في هذا الوقت ، تحتاج إلى إضافة متزامنة للحصول على طريقة وإرجاع استنساخ فقط () من هذا الكائن الخاص. وبهذه الطريقة ، ما يحصل عليه المتصل هو مجرد إشارة إلى نسخة الكائن.
ثلاث طرق للحصول على مراقبة الكائن (القفل) وإخطار ()
في طريقة مؤشر ترابط ، يجب أن تحدد المكالمات إلى WAIT () وإخطار () كائن كائن ، ويجب أن يكون لخيط مراقبة كائن الكائن. أسهل طريقة للحصول على شاشة الكائن هي استخدام الكلمة الرئيسية المتزامنة على الكائن. بعد استدعاء طريقة Wait () ، سيصدر مؤشر الترابط قفل الكائن وإدخال حالة النوم. عندما تستدعي مؤشرات الترابط الأخرى طريقة الإخطار () ، يجب استخدام كائن الكائن نفسه.
للحصول على طرق متعددة مغلقة بواسطة كائن ما ، سيتم اختيار أحدها للاستيقاظ عند استدعاء طريقة الإخطار () ، وسوف يستيقظ الإخطار () من جميع خيوط الانتظار الخاصة به.
package net.mindview.util ؛ import javax.swing.jframe ؛ public waitandnotify {public static void main (] args) {system. frame.setDefaultCloseOperation (jframe. exit_on_close) ؛ 300 ، 100) ؛ {t = WaitandNotifyTher (Waitandnotify .Add (start) ؛ (وقفة) ؛ . = f ؛ if (iswait) wait () ؛ كما هو الحال في الرمز في مربع المثال أعلاه ، إذا تمت إزالة كتلة الكود المتزامن ، فسيقوم التنفيذ بإلقاء استثناء java.lang.illegalmonitorstateException.
بالنظر إلى JDK ، يمكننا أن نرى أن سبب هذا الاستثناء هو أن الخيط الحالي ليس صاحب شاشة الكائن هذا.
يجب أن يتم استدعاء هذه الطريقة فقط من قِبل مؤشر ترابط هو صاحب شاشة الكائن هذه.
1. من خلال تنفيذ طريقة مثيل متزامن لهذا الكائن ، مثل:
الفراغ المزامنة العامة n () {إخطار () ؛ 2. من خلال تنفيذ جسم البيان المتزامن الذي تتم مزامنته على هذا الكائن ، مثل:
public void n () {synchronized (this) {stify () ؛ 3. بالنسبة للكائنات من نوع الفئة ، يمكنك تنفيذ طرق ثابتة متزامنة لهذه الفئة.
عند استدعاء طريقة ثابتة ، لا نقوم بالضرورة بإنشاء كائن مثيل. لذلك ، لا يمكن استخدام هذا لمزامنة الطرق الثابتة ، لذلك يجب استخدام كائن الفئة لمزامنة الطرق الثابتة. مثال آخر لتوضيح:
الطبقة العامة ، تتزامن الفئة العامة المتزامنة {private static boolean flag = true ؛ // طريقة مزامنة كائن الفئة الأولى: // انتبه إلى طريقة التزامن في التعديل الثابت ، الشاشة: synchronizedStatic.Class pri vate Static static void testsyncmethod () {for (int ( i = 0 ؛ // فئة كائن تزامن طريقة 2: private void testsyncblock () {// display يستخدم الفئة الحصول على الشاشة. إنها نفس الطريقة المتزامنة الثابتة تحصل ضمنيًا على شاشة الفصل. Synchronized (Class) ("TestSyncBlock:" + i) ؛ لذلك ، ستنفذ مؤشرات الترابط المختلفة طرقًا مختلفة ، وبهذه الطريقة فقط يمكنك رؤية تأثيرات قفل مختلفة. If (flag) = synchronized Synchronized () ؛ يدير الكود أعلاه نتيجة طريقتين للمزامنة لطباعة 100 أرقام من 0 إلى 99 في نفس الوقت. كتلة. الطريقتان متشابهتان. نظرًا لأن نطاق الطريقة والطريقة الثانية هما فئتان ، فهي حصرية بشكل متبادل. لذلك ، ستكون النتيجة الجارية للبرنامج:
TestSyncMethod: 0testsyncmethod: 1 ... ... TestSyncMethod: 99testsyncblock: 0 ... ... testsyncblock: 99
ومع ذلك ، إذا استبدلنا SynchronizedStatic.
TestSyncBlock: 0testsyncmethod: 0testsyncblock: 1testsyncmethod: 1 ... ... testsyncmethod: 99testsyncblock: 99
هناك نطاقان من الأقفال ، أحدهما هو كائن الفئة والآخر هو الفئة نفسها. في الكود أعلاه ، يتم إعطاء طريقتين لجعل نطاق قفل الفئة ، بحيث يمكن إكمال التزامن بين كائنات مختلفة من نفس الفئة.
لتلخيص ما ورد أعلاه ، يجب الإشارة إلى النقاط التالية:
1. انتظر () ، والإخطار () ، وإخطار () ، يجب تنفيذ جميعهم بموجب فرضية وجود كائن شاشة ، وإلا فإن java.lang.illegalmonitorstateException سيتم طرحها.
2. يمكن أن تنتظر مؤشرات ترابط متعددة كائن واحد في نفس الوقت.
3. الإخطار () هو الاستيقاظ بشكل عشوائي مؤشر ترابط في انتظار الكائن.
4. لا يستيقظ الخيط الذي يستيقظ بواسطة إخطار () فورًا بعد تنفيذ الإخطار () ، ولكن فقط بعد إطلاق الرسائل الإخطار () مراقبة الكائن.
5. لا تزال طرق الكائن هذه بعيدة عن طرق نوم الخيط ومقاطعتها ، لذلك لا تخلط بينها.