1. الاستخدام الأساسي للمزامنة
المزامنة هي الطريقة الأكثر استخدامًا لحل مشاكل التزامن في Java وأسهل طريقة. يحتوي المزامنة على ثلاث وظائف رئيسية: (1) ضمان ترابط رمز مزامنة الوصول الحصري بشكل متبادل (2) تأكد من أن تعديل المتغيرات المشتركة يمكن أن يكون مرئيًا في الوقت المناسب (3) حل مشكلة إعادة الترتيب بشكل فعال. المتزامن له ثلاثة استخدامات متزامنة:
(1) طريقة عادية للتعديل
(2) تعديل الطرق الثابتة
(3) تعديل كتلة الكود
بعد ذلك ، سأستخدم بعض البرامج المثال لتوضيح طرق الاستخدام الثلاثة هذه (من أجل المقارنة ، باستثناء طرق الاستخدام المختلفة للمزامنة ، تكون الرموز الثلاثة الأخرى متسقة بشكل أساسي).
1. لا مزامنة:
قصاصة الكود 1:
package com.paddx.test.concurrent ؛ الفئة العامة synchronizedTest {public void method1 () {system.out.println ("method 1 start") ؛ حاول {system.out.println ("الطريقة 1 تنفيذ") ؛ thread.sleep (3000) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } system.out.println ("method 1 end") ؛ } public void method2 () {system.out.println ("method 2 start") ؛ حاول {system.out.println ("Method 2 Execute") ؛ thread.sleep (1000) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } system.out.println ("Method 2 End") ؛ } public static void main (string [] args) {Final SynchronizedTest test = new SynchronizedTest () ؛ مؤشر ترابط جديد (جديد RunNable () {Override public void run () {test.method1 () ؛}}). start () ؛ مؤشر ترابط جديد (جديد RunNable () {Override public void run () {test.method2 () ؛}}). start () ؛ }}نتيجة التنفيذ هي كما يلي: الموضوع 1 والمعلومات 2 أدخل حالة التنفيذ في نفس الوقت. يتم تنفيذ الموضوع 2 بشكل أسرع من مؤشر الترابط 1 ، لذلك يتم تنفيذ مؤشر الترابط 2 أولاً. في هذه العملية ، يتم تنفيذ مؤشر الترابط 1 والخيط 2 في نفس الوقت.
الطريقة 1 ابدأ
الطريقة 1 تنفيذ
الطريقة 2 ابدأ
الطريقة 2 تنفيذ
الطريقة 2 نهاية
الطريقة 1 نهاية
2. تزامن الطرق الشائعة:
قصاصة الكود الثاني:
package com.paddx.test.concurrent ؛ الفئة العامة synchronizedTest {public synchronized void method1 () {system.out.println ("method 1 start") ؛ حاول {system.out.println ("الطريقة 1 تنفيذ") ؛ thread.sleep (3000) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } system.out.println ("method 1 end") ؛ } method method2 () {system.out.println ("method 2 start") ؛ حاول {system.out.println ("Method 2 Execute") ؛ thread.sleep (1000) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } system.out.println ("Method 2 End") ؛ } public static void main (string [] args) {Final SynchronizedTest test = new SynchronizedTest () ؛ مؤشر ترابط جديد (جديد RunNable () {Override public void run () {test.method1 () ؛}}). start () ؛ مؤشر ترابط جديد (جديد RunNable () {Override public void run () {test.method2 () ؛}}). start () ؛ }}نتيجة التنفيذ على النحو التالي. بعد مقارنتها مع قطاع التعليمات البرمجية ، يمكن أن نرى بوضوح أن مؤشر الترابط 2 يحتاج إلى انتظار تنفيذ Method1 من مؤشر الترابط 1 لإكمال قبل البدء في تنفيذ طريقة Method2.
الطريقة 1 ابدأ
الطريقة 1 تنفيذ
الطريقة 1 نهاية
الطريقة 2 ابدأ
الطريقة 2 تنفيذ
الطريقة 2 نهاية
3. تزامن الطريقة الثابتة (الفئة)
رمز المقتطف الثالث:
حزمة com.paddx.test.concurrent ؛ الفئة العامة SynchronizedTest {public static synchronized void method1 () {system.out.println ("method 1 start") ؛ حاول {system.out.println ("الطريقة 1 تنفيذ") ؛ thread.sleep (3000) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } system.out.println ("method 1 end") ؛ } method 2 () {system.out.println ("method 2 start") ؛ حاول {system.out.println ("Method 2 Execute") ؛ thread.sleep (1000) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } system.out.println ("Method 2 End") ؛ } public static void main (string [] args) {Final SynchronizedTest test = new SynchronizedTest () ؛ اختبار متزامن النهائي 2 = synchronizedTest () جديد ؛ مؤشر ترابط جديد (جديد RunNable () {Override public void run () {test.method1 () ؛}}). start () ؛ مؤشر ترابط جديد (جديد RunNable () {Override public void run () {test2.method2 () ؛}}). start () ؛ }}نتيجة التنفيذ على النحو التالي. إن مزامنة الأساليب الثابتة هي في الأساس مزامنة الفئات (الطرق الثابتة هي في الأساس طرق للفئة ، وليس الطرق على الكائنات). لذلك ، حتى إذا كان الاختبار والاختبار 2 ينتميان إلى كائنات مختلفة ، فإن كلاهما ينتمي إلى حالات من فئة SynchronizedTest ، لذلك لا يمكن تنفيذ الطريقة 1 و Method2 بشكل متتابع ، ولا يمكن تنفيذها بشكل متزامن.
الطريقة 1 ابدأ
الطريقة 1 تنفيذ
الطريقة 1 نهاية
الطريقة 2 ابدأ
الطريقة 2 تنفيذ
الطريقة 2 نهاية
4. تزامن كتلة الكود
Code Snippet Four:
package com.paddx.test.concurrent ؛ الفئة العامة synchronizedTest {public void method1 () {system.out.println ("method 1 start") ؛ حاول {Synchronized (this) {system.out.println ("الطريقة 1 تنفيذ") ؛ thread.sleep (3000) ؛ }} catch (interruptedException e) {E.PrintStackTrace () ؛ } system.out.println ("method 1 end") ؛ } public void method2 () {system.out.println ("method 2 start") ؛ حاول {Synchronized (this) {system.out.println ("method 2 execute") ؛ thread.sleep (1000) ؛ }} catch (interruptedException e) {E.PrintStackTrace () ؛ } system.out.println ("Method 2 End") ؛ } public static void main (string [] args) {Final SynchronizedTest test = new SynchronizedTest () ؛ مؤشر ترابط جديد (جديد RunNable () {Override public void run () {test.method1 () ؛}}). start () ؛ مؤشر ترابط جديد (جديد RunNable () {Override public void run () {test.method2 () ؛}}). start () ؛ }}نتيجة التنفيذ على النحو التالي. على الرغم من أن كلا من مؤشر الترابط 1 و مؤشر الترابط 2 أدخلوا الطريقة المقابلة وبدء التنفيذ ، إلا أن مؤشر الترابط 2 يحتاج إلى انتظار تنفيذ كتلة التزامن في مؤشر الترابط 1 لإكمال قبل إدخال كتلة التزامن.
الطريقة 1 ابدأ
الطريقة 1 تنفيذ
الطريقة 2 ابدأ
الطريقة 1 نهاية
الطريقة 2 تنفيذ
الطريقة 2 نهاية
2. مبدأ متزامن
إذا كان لا يزال لديك أي أسئلة حول نتائج التنفيذ أعلاه ، فلا تقلق. دعونا أولاً نفهم مبدأ المزامنة ، ثم ننظر إلى الوراء في الأسئلة أعلاه لنرى في لمحة. دعونا أولاً نلقي نظرة على كيفية مزامنة التعليمات البرمجية المتزامنة مع كتل التعليمات البرمجية عن طريق فك الرمز التالي:
package com.paddx.test.concurrent ؛ الفئة العامة synchronizedDemo {public void method () {synchronized (this) {system.out.println ("method 1 start") ؛ }}}نتيجة فك الارتباط:
فيما يتعلق بدور هاتين التعليمات ، نشير مباشرة إلى الوصف في مواصفات JVM:
المراقبة:
يرتبط كل كائن مع الشاشة. يتم قفل الشاشة إذا وفقط إذا كان لديه مالك. الخيط الذي ينفذ محاولات المراقبة لاكتساب ملكية الشاشة المرتبطة بـ ObjectRef ، على النحو التالي: • إذا كان عدد إدخال الشاشة المرتبط بـ ObjectRef صفرًا ، فإن مؤشر الترابط يدخل الشاشة ويحدد عدد الإدخال إلى واحد. يكون مؤشر الترابط هو صاحب الشاشة. • إذا كان مؤشر الترابط بالفعل يمتلك الشاشة المرتبطة بـ ObjectRef ، فإنه يعيد إدخال الشاشة ، ويزيد من عدد الإدخال الخاص به. • إذا كان مؤشر ترابط آخر يمتلك بالفعل الشاشة المرتبطة بـ ObjectRef ، فإن كتل مؤشر الترابط حتى يصبح عدد إدخال الشاشة صفراً ، ثم يحاول الحصول على ملكية.
المعنى العام لهذا المقطع هو:
كل كائن لديه قفل شاشة (شاشة). عندما يتم احتلال الشاشة ، سيتم قفلها. عندما ينفذ مؤشر الترابط تعليمات المراقبة ، فإنه يحاول الحصول على ملكية الشاشة. العملية كما يلي:
1. إذا كان عدد إدخال الشاشة هو 0 ، فإن مؤشر الترابط يدخل الشاشة ، ثم يقوم بتعيين رقم الإدخال على 1 ، فإن مؤشر الترابط هو مالك الشاشة.
2. إذا كان الخيط يمتلك بالفعل الشاشة ويعود إلى فقط ، فسيتم إضافة عدد الدخول إلى الشاشة إلى 1.
3. إذا كانت المواضيع الأخرى قد احتلت الشاشة ، فإن الخيط يدخل حالة حظر حتى يكون عدد إدخال الشاشة 0 ، ثم حاول الحصول على ملكية الشاشة مرة أخرى.
monitorexit:
يجب أن يكون مؤشر الترابط الذي ينفذ monitorexit هو مالك الشاشة المرتبطة بالمثال المشار إليه بواسطة ObjectRef.The يخفض مؤشر الترابط عدد الإدخال للشاشة المرتبطة بـ ObjectRef. إذا كانت قيمة عدد الإدخال كنتيجة لذلك صفرًا ، فإن الخيط يخرج من الشاشة ولم يعد مالكه. يسمح لمروح أخرى تمنع للدخول إلى الشاشة بمحاولة القيام بذلك.
المعنى العام لهذا المقطع هو:
يجب أن يكون مؤشر ترابط تنفيذ Monitorexit هو مالك الشاشة المقابلة لـ ObjectRef.
عند تنفيذ التعليمات ، يتم تقليل عدد الشاشة التي تدخل بمقدار 1. إذا كان عدد الشاشة التي تدخل 0 بعد الانخفاض بمقدار 1 ، فإن الخيط يخرج من الشاشة ولم يعد مالك هذا الشاشة. يمكن لخيوط أخرى تم حظرها بواسطة هذه الشاشة محاولة الحصول على ملكية هذا الشاشة.
من خلال هاتين الفقرتين من الوصف ، يجب أن نكون قادرين على رؤية مبدأ التنفيذ المتزامن بوضوح. يتم الانتهاء من الطبقة الدلالية الأساسية من المزامنة من خلال كائن شاشة. في الواقع ، انتظر/الإخطار والأساليب الأخرى تعتمد أيضًا على كائنات الشاشة. هذا هو السبب في أنه يمكن استدعاء طرق فقط مثل الانتظار/الإخطار في الكتل أو الأساليب المتزامنة ، وإلا سيتم طرح استثناء من java.lang.illegalmonitorstateException.
دعونا نلقي نظرة على نتائج إلغاء التزامن لطريقة التزامن:
رمز المصدر:
package com.paddx.test.concurrent ؛ الطبقة العامة synchronizedmethod {public synchronized void method () {system.out.println ("Hello World!") ؛ }}نتيجة فك الارتباط:
انطلاقًا من نتائج فك الإلغاء ، لم يتم إكمال مزامنة الطريقة من خلال التعليمات المراقبة و Monitorexit (من الناحية النظرية ، يمكن أيضًا تنفيذها من خلال هاتين التعليمات). ومع ذلك ، بالمقارنة مع الطرق العادية ، تتم إضافة المعرف ACC_Synchronized إلى تجمعه الثابت. تنفذ JVM تزامن الأساليب بناءً على هذا المعرف: عندما يتم استدعاء الطريقة ، ستتحقق تعليمات الاتصال ما إذا كان يتم تعيين علامة الوصول إلى ACC_Synchronized للطريقة. إذا تم تعيينها ، فسيحصل مؤشر ترابط التنفيذ أولاً على الشاشة ، ثم تنفيذ هيئة الطريقة بعد تنفيذ الطريقة بنجاح. بعد تنفيذ الطريقة ، سيتم إصدار الشاشة. أثناء تنفيذ الطريقة ، لا يمكن لأي مؤشر ترابط آخر الحصول على كائن الشاشة نفسه بعد الآن. في الواقع ، لا يوجد فرق في الجوهر ، لكن مزامنة الطريقة هي وسيلة ضمنية لتحقيق ذلك دون الحاجة إلى القيام من خلال رمز Bytecode.
3. شرح نتائج العملية
مع فهم مبدأ المزامنة ، يمكنك بسهولة حلها من خلال النظر إلى البرنامج أعلاه.
1. نتائج الرمز 2:
على الرغم من أن Method1 و Method2 من الطرق المختلفة ، إلا أن كلتا الطريقتين تتم مزامنتها ويتم استدعاؤهما من خلال نفس الكائن. لذلك ، قبل الاتصال ، تحتاج إلى التنافس على القفل (الشاشة) على نفس الكائن ، بحيث يمكنك فقط الحصول على الأقفال بشكل متبادل بشكل خاص. لذلك ، لا يمكن تنفيذ Method1 و Method2 إلا بالتتابع.
2. شريحة الكود 3 نتائج:
على الرغم من أن الاختبار والاختبار 2 ينتمي إلى كائنات مختلفة ، إلا أن الاختبار واختبار 2 ينتمي إلى مثيلات مختلفة من نفس الفئة. نظرًا لأن Method1 و Method2 ينتميان إلى طرق المزامنة الثابتة ، فأنت بحاجة إلى الحصول على الشاشة في نفس الفئة (كل فئة تتوافق فقط مع كائن فئة واحد) ، بحيث يمكنك فقط تنفيذ متتابع.
3. نتائج الرمز 4:
بالنسبة لمزامنة كتل التعليمات البرمجية ، من الضروري بشكل أساسي الحصول على شاشة الكائن في الأقواس بعد الكلمة الرئيسية المتزامنة. نظرًا لأن محتويات الأقواس الموجودة في هذا الرمز هي ، ويتم استدعاء Method1 و Method2 من خلال نفس الكائن ، لذلك قبل إدخال كتلة التزامن ، تحتاج إلى التنافس على الأقفال على نفس الكائن ، بحيث لا يمكن تنفيذ كتلة التزامن إلا بالتسلسل.
أربعة ملخص
المزامنة هي الطريقة الأكثر استخدامًا لسلامة مؤشرات الترابط في البرمجة المتزامنة Java ، وهي بسيطة نسبيًا للاستخدام. ومع ذلك ، إذا استطعنا أن نفهم مبادئها بعمق ولدينا بعض الفهم للمعرفة الأساسية مثل أقفال الشاشة ، يمكن أن تساعدنا في استخدام الكلمات الرئيسية المتزامنة بشكل صحيح ، ومن ناحية أخرى ، يمكن أن تساعدنا أيضًا على فهم آلية برمجة التوافق بشكل أفضل ، وتساعدنا على اختيار استراتيجيات توافق أفضل لإكمال المهام في ظل ظروف مختلفة. يمكنك أيضًا التعامل بهدوء مع العديد من المشكلات المتزامنة التي تواجهها في الحياة اليومية.