نحن نعلم أنه يجب إكمال العمليات المتزامنة التي تنفذها Java من قبل وحدة المعالجة المركزية لدينا. في غضون ذلك ، قمنا بتجميع رمز مصدر Java في ملف .class ، ثم تم تحميله ، ثم تنفيذه بواسطة محرك تنفيذ الجهاز الظاهري ، تم تفسيره على أنه لغة تجميع ، ثم تحويله إلى تعليمات نظام التشغيل ، ثم تم تحويلها إلى 1 و 0 ، وأخيراً يتم التعرف على وحدة المعالجة المركزية وتنفيذها.
عندما نذكر توافق Java ، لا يسعنا إلا التفكير في الكلمات الرئيسية المشتركة في Java: متطايرة ومزامنة. بعد ذلك ، سنقوم بتحليلها من هاتين كلمتين الإغلاق:
مبدأ التنفيذ الأساسي للمتقلبة
مبادئ التنفيذ وتطبيقات المزامنة
متقلب
عند الحديث عن المتقلبة ، فإن المقابلة هي السؤال المفضل لطرحه في مقابلات Java. عندما نراها ، فإن أول شيء نفكر فيه هو الحفاظ على الرؤية بين المواضيع. إنها متزامنة خفيفة الوزن ، والتي يمكن أن تحل محل متزامن في بعض الحالات.
دور المتقلبة:
بالنسبة للمتغير الذي تم تعديله بواسطة متطاير ، سيضمن نموذج ذاكرة Java أن تكون القيم المتغيرة التي تراها جميع مؤشرات الترابط متسقة.
كيف يعمل تقلب:
يمكننا تحديد متغير متطاير ، وتعيين قيمه ، واستخدام الأدوات للحصول على تعليمات التجميع التي تم إنشاؤها بواسطة برنامج التحويل البرمجي JIT. سنجد أنه عند الكتابة إلى المتغير المتطاير ، سيكون هناك تعليمات إضافية: تعليمات مسبقة بالقفل:
تسبب تعليمات بادئة القفل شيئين مرة أخرى إلى المعالج متعدد النواة:
① اكتب بيانات خط ذاكرة التخزين المؤقت المعالج الحالي للذاكرة.
② هذه عملية الذاكرة للكتابة ستؤدي إلى إبطال عنوان الذاكرة المخزنة في البيانات في وحدات المعالجة المركزية الأخرى.
عندما نعرف النقطتين أعلاه ، ليس من الصعب علينا أن نفهم آلية متغير Volatie.
تحت معالجات متعددة ، من أجل التأكد من أن ذاكرة التخزين المؤقت لكل معالج متسقة ، سيتم تنفيذ بروتوكول تناسق ذاكرة التخزين المؤقت. كل معالج يستنشق البيانات التي تم نشرها على الحافلة للتحقق مما إذا كانت القيمة المخزنة قد انتهت صلاحيتها.
متزامن
عند التفكير في التزامن متعدد الخيوط ، يتم مزامنة أول شيء أفكر فيه. ترجمت كما المزامنة. نعلم جميعا أنه قفل الوزن الثقيل. عند استخدامه لطريقة أو كتلة رمز ، عندما يحصل مؤشر ترابط على هذا القفل ، فإن مؤشرات الترابط الأخرى ستقع في حالة مع وقف التنفيذ ، والتي ستظهر في حالة النوم في Java. نعلم جميعًا أنه يجب نقل نظام التعليق ووقت التشغيل للموضوع إلى حالة kernel لنظام التشغيل (المقابل لحالة kernel هو حالة المستخدم) ، وهي مضيعة بشكل خاص لموارد وحدة المعالجة المركزية ، وبالتالي فإن قفل الوزن الثقيل هذا حقيقي!
ومع ذلك ، بعد Java SE 1.6 ، قام فريق صيانة Java بإجراء سلسلة من التحسينات عليها (تتم مناقشة هذه التحسينات واحدة تلو الأخرى) ، لذلك ليس هذا "ثقيلًا" ، وأصبحت قفل Reenrant ، الذي كان له مزايا في الماضي ، أقل فائدة (إعادة إدراج).
دعنا نتحدث عن المزامنة في الجوانب التالية:
أساسيات المزامنة لتحقيق المزامنة
كيف تنفس الأدوات المتزامنة القفل
قفل إيجابي ، قفل خفيف الوزن (قفل تدور) ، قفل الوزن الثقيل
قفل الترقية
كيفية تنفيذ العمليات الذرية في جافا
① أساسيات المزامنة لتحقيق التزامن:
يمكننا أن نرى متزامنًا في التطوير أو في رمز مصدر Java ، مثل علامة التجزئة و StringBuilder وغيرها من الأماكن. هناك طريقتان مشتركتان:
ⅰ ، طريقة التزامن
يجب مزامنة طريقة التزامن فقط قبل الطريقة. عندما ينفذها مؤشر ترابط واحد ، سوف تسقط مؤشرات الترابط الأخرى في الانتظار حتى تصدر القفل. يمكن تقسيم استخدام الطرق إلى نوعين: لطرق التزامن العادية وللطرق الساكنة. الفرق بينهما هو أن الكائنات المقفلة مختلفة. الموضع المقفل للطرق العادية هو الكائن الحالي ، والموضع المقفل للطرق الثابتة هو كائن الفئة للفئة الحالية.
ⅱ ، كتلة طريقة التزامن
تقوم طريقة المزامنة بإغلاق الكائن الذي تم تكوينه في الأقواس بعد مزامنة. يمكن أن يكون هذا الكائن قيمة وأي متغير أو كائن.
② كيف تزامن الأدوات القفل:
في مواصفات JVM ، يمكنك رؤية مبدأ التنفيذ المتماسك في JVM. JVM ينفذ مزامنة طرق التزامن وكتل التعليمات البرمجية بناءً على إدخال كائن الشاشة والخروج منها. يتم تنفيذ كتلة التعليمات البرمجية باستخدام تعليمات MoniterEnter و Monitorexit. لا يتم تقديم طريقة التزامن على وجه التحديد في مواصفات JVM. ومع ذلك ، أعتقد أن المبادئ المحددة يجب أن تكون مختلفة. إنه ليس أكثر من تجميع رمز مصدر Java في ملف فئة ، ووضع علامة على الطريقة المتزامنة في ملف الفئة Bytecode. سيتم مزامنة هذه الطريقة عندما يقوم محرك Bytecode بتنفيذ هذه الطريقة.
قفل ③deflection ، قفل خفيف الوزن (قفل تدور) ، قفل الوزن الثقيل:
قبل الحديث عن الأقفال ، نحتاج إلى معرفة رأس كائن Java ورأس كائن Java:
يتم تخزين القفل المستخدم بواسطة متزامن في رأس كائن Java. يحتوي رأس كائن Java على 32bit/64bit (اعتمادًا على عدد أجزاء البتات من نظام التشغيل). هناك مساحات 2bit في Markword لتمثيل حالة Lock 00 ، 01 ، 10 ، 11 ، على التوالي ، تمثل أقفال خفيفة الوزن ، أقفال التحيز ، أقفال الوزن الثقيل ، وعلامات GC.
قفل إيجابي: يسمى القفل الإيجابي قفل غريب الأطوار. من الاسم يمكننا أن نرى أنه قفل يميل نحو موضوع معين.
في التطوير الفعلي ، وجدنا أن التزامن متعدد الخيوط ، يتم تنفيذ معظم أساليب التزامن من خلال نفس الخيط ، وأن احتمال أن تكون مؤشرات ترابط متعددة تتنافس على طريقة واحدة منخفضة نسبيًا ، لذلك فإن عملية الاستحواذ المتكررة وإصدار الأقفال سيؤدي إلى الكثير من نفايات الموارد. لذلك ، من أجل جعل الخيط يحصل على قفل بتكلفة أقل ، يتم تقديم قفل التحيز. عندما يصل مؤشر ترابط إلى كتلة التزامن ويحصل على قفل ، سيتم تخزين معرف مؤشر ترابط قفل التحيز في سجل القفل في إطار مكدس رأس الكائن وخيط. في المستقبل ، عندما يدخل الخيط ويخرج من كتلة التزامن ، فإنه لا يحتاج إلى إجراء عمليات CAS للقفل وفتح. من الضروري فقط التحقق مما إذا كان هناك قفل تحيز يشير إلى كلمة markword الحالية في رأس الكائن (في Markword ، هناك جزء من علامة قفل التحيز للإشارة إلى ما إذا كان الكائن الحالي يدعم قفل التحيز. يمكننا استخدام معلمة JVM لتعيين قفل التحيز).
فيما يتعلق بإصدار الأقفال المتحيزة ، تستخدم الأقفال المتحيزة آلية إطلاق القفل حتى توجد المنافسة ، وبالتالي فإن الخيط الذي يحمل القفل المتحيز سيطلق القفل عندما تحاول الخيوط الأخرى التنافس على الأقفال المتحيزة.
ملاحظة: في Java6 ، 7 ، بدأ قفل التحيز افتراضيًا
قفل خفيف الوزن:
القفل خفيف الوزن هو أنه قبل تنفيذ كتلة التزامن ، سيقوم JVM بإنشاء مساحة لتخزين سجل القفل في إطار المكدس للخيط الحالي ، ونسخ كلمة الكلمة في رأس الكائن فيه. بعد ذلك ، سيحاول مؤشر الترابط استبدال كلمة markword في رأس الكائن بمؤشر إلى سجل القفل. إذا نجح ، يحصل الخيط الحالي على القفل. إذا فشلت ، فهذا يعني أن مؤشرات الترابط الأخرى تتنافس على القفل ، وسيتم تدور مؤشر الترابط الحالي للحصول على القفل.
④ قفل الترقية:
إذا لم يتمكن الخيط الحالي من تجربة الطريقة أعلاه للحصول على القفل ، فهذا يعني أن القفل الحالي في المنافسة وسيتم ترقية القفل إلى قفل الوزن الثقيل.
الفرق بين القفل خفيف الوزن والقفل المتحيز:
تستخدم الأقفال الخفيفة الوزن عمليات CAS للتخلص من الطفورات المستخدمة في المزامنة دون منافسة ، في حين أن الأقفال المتحيزة تزيل المزامنة بأكملها دون منافسة دون منافسة ، وحتى عمليات CAS لم تتم!
⑤ كيفية تنفيذ العمليات الذرية في جافا:
قبل فهم كيفية تنفيذ Java العمليات الذرية ، نحتاج إلى معرفة كيفية تنفيذ المعالجات العمليات الذرية:
تنقسم المعالجات عمومًا إلى طريقتان لأداء العمليات الذرية: قفل ذاكرة التخزين المؤقت وقفل الحافلات ، من بينها قفل ذاكرة التخزين المؤقت أفضل ، في حين أن قفل الحافلات يستهلك الموارد. (لن نشرح الكثير عن طريقتي القفل هنا ، ولكن سيكون هناك تفسيرات مفصلة في نظام التشغيل)
يستخدم Java (في الغالب) حلقة CAS لتنفيذ العمليات الذرية ، ولكن استخدام CAS لتنفيذ العمليات الذرية سيؤدي أيضًا إلى بعض المشكلات الكلاسيكية التالية:
1) مشكلة ABA
يتم توفير فئة AtomicStampedReference في JDK لحل (توفير التحقق من المراجع المتوقعة والأعلام المتوقعة)
2) وقت الدورة الطويل وعالي النفقات العامة
لا يمكن حل هذا ، هذه مشكلة شائعة في الدورة الدموية
3) يمكن ضمان فقط العمليات الذرية لمتغير مشترك
يتم توفير مؤتمر ذري في JDK لحل المشكلة ، ووضع متغيرات مشتركة متعددة في فئة لعمليات CAS.