يعد التزامن أمرًا بالغ الأهمية لتطوير تطبيقات قوية وقابلة للتطوير يمكنها تنفيذ العديد من العمليات المتزامنة. ومع ذلك، يجب دفع ثمن لهذا من حيث التزامن. إنه يتحمل تكاليف الأداء بسبب النفقات العامة المصاحبة للحصول على الأقفال وتحريرها. ولتخفيف تكاليف الأداء هذه، تم دمج العديد من التحسينات في JVM، في العديد من النكهات، مثل القفل المتحيز، وإزالة القفل، وخشونة القفل، وفكرة الأقفال خفيفة الوزن والثقيلة.
في هذه المقالة، نرى هذه التحسينات بمزيد من التفصيل، ونتناول كيفية تحسين المزامنة في تطبيقات Java متعددة الخيوط.
أساسيات قفل جافا
في Java، تضمن مزامنة الكتل أو الأساليب أن مؤشر ترابط واحد فقط يمكنه تنفيذ قسم مهم من التعليمات البرمجية في المرة الواحدة. وهذا مهم بشكل خاص عند النظر في مشاركة الموارد داخل بيئة متعددة مؤشرات الترابط. تنفذ Java ذلك من خلال الاعتماد على الأقفال الجوهرية - أو في بعض الأحيان، يطلق عليها اسم الشاشات المرتبطة بالكائنات أو الفئات التي تساعد في إدارة الوصول إلى سلاسل الرسائل باستخدام الكتل المتزامنة.
على الرغم من أن المزامنة ضرورية لسلامة سلسلة المحادثات، إلا أنها قد تكون مكلفة للغاية عندما يكون التنافس منخفضًا أو غائبًا تمامًا. وهذا هو المكان الذي تدخل فيه تحسينات JVM في الإطار. وبالتالي، فإن ذلك يقلل من تكلفة القفل ويعزز الأداء العام.
1. القفل المتحيز
ما هو القفل المتحيز؟
القفل المتحيز هو تحسين يستهدف تقليل الحمل الزائد لاكتساب القفل. تم تحسينه لتقليل تكلفة الحصول على القفل، والتي يهيمن عليها خيط واحد أو يتم الوصول إليها إلى حد كبير عن طريق خيط واحد. غالبًا ما تحصل مثل هذه البرامج على الأقفال وتحررها بواسطة نفس مؤشر الترابط دون أي منافسة مع سلاسل رسائل أخرى. يمكن لـ JVM التعرف على هذا النمط وتحيز القفل إلى هذا الخيط المحدد؛ يأتي الحصول على القفل مجانًا تقريبًا.
كيف يعمل القفل المتحيز؟
إذا تم تمكين القفل المتحيز، ففي المرة الأولى التي يحصل فيها الخيط على قفل، فإنه يجعل هذا القفل متحيزًا تجاه هذا الخيط. يتم تسجيل هوية مؤشر الترابط في رأس كائن القفل، ولا تتضمن عمليات اكتساب القفل اللاحقة بواسطة هذا الخيط أي مزامنة على الإطلاق - فهي تتحقق فقط مما إذا كان القفل متحيزًا تجاه مؤشر الترابط الحالي، وهي عملية سريعة جدًا وغير قابلة للحظر .
إذا حاول مؤشر ترابط آخر الحصول على القفل، فسيتم إلغاء الانحياز ويعود JVM إلى آلية القفل القياسية غير المتحيزة. في هذه المرحلة، أصبح الآن قفلًا قياسيًا، وسيتعين على الخيط الثاني الحصول عليه من خلال عملية قفل قياسية.
فوائد القفل المتحيز
الأداء: يعد الحصول على نفس الخيط على قفل متحيز بمثابة اكتساب قفل مجاني تقريبًا.
وبالتالي، ليست هناك حاجة إلى معالجة التنافس لأن المواضيع الأخرى ليس لديها فرصة للمشاركة في الحصول على القفل.
الحمل السفلي: لا يلزم تغيير حالة القفل أو تعديل البيانات التعريفية المتعلقة بالمزامنة إلا في حالة التنافس.
متى يتم استخدام القفل المتحيز؟
يعد القفل المتحيز مفيدًا في التطبيقات التي يتم فيها الوصول إلى الأقفال بشكل أساسي من خلال نفس مؤشر الترابط، مثل التطبيقات ذات مؤشر الترابط المفرد أو التطبيق الذي يحتوي على تنافس قفل منخفض ضمن مؤشرات ترابط متعددة. يتم تمكينه افتراضيًا في معظم أجهزة JVM.
كيفية تعطيل القفل المتحيز
يتم تمكين القفل المتحيز افتراضيًا ولكن يمكن أيضًا تعطيله باستخدام علامة JVM كما هو موضح أدناه:
-XX:-استخدام القفل المتحيز
2. إزالة القفل
ما هو إلغاء القفل؟
يعد إلغاء القفل تحسينًا قويًا للغاية حيث يقوم JVM بإزالة بعض المزامنة (الأقفال) غير الضرورية تمامًا. سيقوم بفحص الكود بحثًا عن أي فرص أثناء تجميع JIT الخاص به حيث يكتشف أن المزامنة ليست ضرورية. يحدث هذا عادةً عندما يتم الوصول إلى القفل من خلال مؤشر ترابط واحد فقط، أو عندما لا يشارك الكائن الذي سيتم استخدام JVM لمزامنته نفس الكائن داخل سلاسل رسائل مختلفة. بمجرد أن يرى JVM أنه لم يعد مطلوبًا، فإنه يقوم بإزالة القفل.
كيف يعمل إلغاء القفل؟
في مرحلة تحليل الهروب لتجميع JIT، يتحقق JVM مما إذا كان الكائن يقتصر على مؤشر ترابط واحد أو يتم استخدامه فقط في سياق محلي. إذا كان من الممكن إزالة المزامنة على هذا الكائن لأن الكائن لا يفلت من نطاق مؤشر الترابط الذي أنشأه، فسيكون الأمر كذلك.
على سبيل المثال، إذا تم إنشاء كائن واستخدامه بالكامل ضمن طريقة ما (وليس مشاركته عبر سلاسل العمليات)، يدرك JVM أنه لا يمكن لخيط آخر الوصول إلى الكائن، وبالتالي تكون جميع عمليات المزامنة زائدة عن الحاجة. في مثل هذه الحالة، يقوم مترجم JIT ببساطة بإزالة القفل تمامًا.
Zero Locking Overhead: سيؤدي التخلص من المزامنة غير الضرورية أيضًا إلى منع JVM من دفع تكلفة الحصول على الأقفال وتحريرها في المقام الأول.
إنتاجية أعلى: يمكن أن يؤدي التزامن الميت في بعض الأحيان إلى إنتاجية أعلى للتطبيق، خاصة إذا كان الكود يحتوي على العديد من الكتل المتزامنة.
ألق نظرة على هذا الجزء من الكود:
public void someMethod() { StringBuilder sb = new StringBuilder(); synchronized (sb) { sb.append("Hello"); sb.append("World"); } }
في هذه الحالة، المزامنة على sb ليست ضرورية نظرًا لأن StringBuilder يتم استخدامه فقط داخل someMethod ولا تتم مشاركته بين سلاسل الرسائل الأخرى. من خلال النظر إلى هذا، يمكن لـ JVM إجراء تحليل الهروب لإزالة القفل.
3. قفل الخشنة
ما هو قفل التخشين؟
يعد تقليص القفل بمثابة تحسين حيث يقوم JVM بتوسيع نطاق القفل لتغطية المزيد من أجزاء التعليمات البرمجية بدلاً من الحصول بشكل مستمر على القفل وتحريره في حلقات أو أقسام صغيرة من التعليمات البرمجية.
عمل التخشين القفلي
إذا اكتشف JVM أن حلقة ضيقة أو عدة كتل تعليمات برمجية متجاورة تكتسب قفلًا وتحرره بشكل متكرر، فيمكنه جعل القفل خشنًا عن طريق إخراج القفل خارج الحلقة أو عبر عدة كتل من التعليمات البرمجية. وهذا يجعل الحصول المتكرر على القفل غير القابل للقفل وإصداره مكلفًا ويمكّن الخيط من الاحتفاظ بالقفل لمزيد من التكرارات.
مثال الكود: قفل التخشين
ضع في اعتبارك مقتطف الشفرة هذا:
for (int i = 0; iيؤدي خشونة القفل إلى دفع اكتساب القفل خارج الحلقة، وبالتالي فإن الخيط يكتسب القفل مرة واحدة فقط:
synchronized (lock) { for (int i = 0; iيمكن لـ JVM تحسين الأداء بشكل كبير عن طريق تجنب المزيد من عمليات الاستحواذ والإصدارات للقفل.
فوائد خشونة القفل
حرية أقل في قفل النفقات العامة: يؤدي التخشين إلى تجنب عمليات اكتساب القفل وإصداراته، خاصة في كود نقطة الاتصال، مثل الحلقات التي تم تكرارها آلاف المرات.
تحسين الأداء:
يؤدي القفل لفترة أطول إلى تحسين الأداء مقارنةً بالسيناريو الذي، بدون القفل، سيتم الحصول على هذا القفل وتحريره عدة مرات.4. أقفال خفيفة الوزن وثقيلة الوزن
يستخدم JVM تقنيتين مختلفتين للقفل بناءً على درجة التنافس بين الخيوط. وتشمل هذه التقنيات أقفال خفيفة الوزن وأقفال ثقيلة الوزن.
قفل خفيف الوزن
يتم إجراء القفل الخفيف في حالة عدم وجود قفل التنافس، مما يعني أن مؤشر ترابط واحد فقط يحاول الحصول على هذا القفل. في مثل هذه السيناريوهات، يقوم JVM بتحسين عملية الاستحواذ باستخدام عملية CAS عند محاولة الحصول على القفل، والذي يمكن أن يحدث بدون مزامنة ثقيلة الوزن.
قفل الوزن الثقيل
في حالة رغبة عدة سلاسل في الحصول على نفس القفل؛ أي أن هناك خلافًا، حيث يقوم JVM بتصعيد هذا إلى قفل الوزن الثقيل. قد يتضمن ذلك حظر سلاسل الرسائل على مستوى نظام التشغيل وإدارتها باستخدام أساسيات المزامنة على مستوى نظام التشغيل. تعتبر الأقفال ذات الوزن الثقيل أبطأ لأنها تتطلب في الواقع من نظام التشغيل إجراء تبديل السياق، بالإضافة إلى إدارة سلاسل الرسائل.
قفل التصعيد
إذا نشأ خلاف على قفل خفيف الوزن، فقد يقوم JVM بتصعيده إلى قفل ثقيل الوزن. التصعيد هنا يعني التبديل من القفل السريع على مستوى المستخدم إلى قفل أكثر تكلفة على مستوى نظام التشغيل والذي يتضمن حظر سلسلة الرسائل.
فوائد الأقفال خفيفة الوزن
الاستحواذ السريع على القفل: عندما لا يكون هناك خلاف، تكون الأقفال خفيفة الوزن أسرع بكثير من الأقفال ثقيلة الوزن لأنها تتجنب المزامنة على مستوى نظام التشغيل.
تقليل الحظر: مع عدم وجود أي تعارضات، لا يتم حظر سلاسل الرسائل وتزداد بشكل خطي مع زمن وصول أقل.
عيوب الأقفال ذات الوزن الثقيل
الأعباء العامة على الأداء: تتحمل الأقفال ذات الوزن الثقيل تكلفة حظر سلسلة المحادثات، وتبديل السياق، وإيقاظ المواضيع مع تدهور الأداء في أنظمة التنافس العالية جدًا.
تساعد كل هذه التحسينات JVM على تحسين الأداء في التطبيقات متعددة الخيوط، بحيث يمكن للمطورين الآن كتابة تعليمات برمجية آمنة ومتزامنة دون التضحية بالكثير من تكاليف المزامنة. يمكن أن يساعد فهم هذه التحسينات المطورين على تصميم أنظمة أكثر كفاءة، خاصة في الحالات التي تفرض عقوبة عالية الأداء للقفل.
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3