نص
عند البرمجة في بيئة متزامنة ، يلزم وجود آلية قفل لمزامنة العمليات بين مؤشرات ترابط متعددة لضمان الوصول الحصري للموارد المشتركة بشكل متبادل. يمكن أن تسبب القفل تلف الأداء ، والذي يبدو أنه معروف جيدًا. ومع ذلك ، فإن قفل نفسه لا يجلب الكثير من استهلاك الأداء ، والأداء هو أساسا عملية الحصول على الأقفال في المواضيع. إذا كان هناك موضوع واحد فقط يتنافس على الأقفال ولا توجد منافسة متعددة الخيوط في هذا الوقت ، فسيتم تحسين JVM ، ويمكن تجاهل استهلاك الأداء الناجم عن القفل. لذلك ، فإن توحيد تشغيل القفل ، وتحسين طريقة الاستخدام في القفل ، وتجنب منافسة الخيط غير الضرورية ، لا يمكن أن يحسن أداء البرنامج فحسب ، بل يتجنب أيضًا إمكانية المميتة المميتة الناتجة عن قفل غير منتظم وتحسين متانة البرنامج. ما يلي يشرح العديد من أفكار تحسين القفل.
1. حاول عدم قفل الطريقة
عند إضافة قفل إلى وظيفة العضو العادي ، يحصل مؤشر الترابط على قفل كائن الكائن الذي توجد فيه الطريقة. في هذا الوقت سيتم قفل الكائن بأكمله. هذا يعني أيضًا أنه إذا كانت أساليب المزامنة المتعددة التي يوفرها هذا الكائن لخدمات مختلفة ، فمنذ أن يتم قفل الكائن بأكمله ، عند معالجة شركة واحدة ، يجب أن تنتظر مؤشرات ترابط الأعمال الأخرى غير ذات الصلة أيضًا. يوضح المثال التالي هذا:
تحتوي فئة LockMethod على طريقتين للمزامنة ، والتي تسمى في عمليتين تجاريتين:
الفئة العامة lockmethod {public synchronized void busia () {for (int i = 0 ؛ i <10000 ؛ i ++) {system.out.println (thread.currentThread (). getName ()+"تعامل مع الأعمال a:"+i) ؛ }} Public Synchronized void busib () {for (int i = 0 ؛ i <10000 ؛ i ++) {system.out.println (thread.currentThread (). getName ()+"تعامل مع الأعمال B:"+i) ؛ }}}Bussa عبارة عن فئة مؤشرات ترابط تستخدم للتعامل مع الأعمال التجارية وتدعو طريقة busia () من lockmethod:
الفئة العامة bussb يمتد الموضوع {lockmethod lockmethod ؛ صفقة void (lockmethod lockmethod) {this.lockMethod = lockmethod ؛ } Override public void run () {super.run () ؛ lockmethod.busib () ؛ }}تستخدم فئة TraclockMethod Bussa و BUSSB لمعالجة الأعمال:
الطبقة العامة testlockmethod يمتد الموضوع {public static void main (string [] args) {lockmethod lockmethod = new LockMethod () ؛ Bussa Bussa = Bussa () ؛ BUSSB BUSSB = BUSSB جديد () ؛ bussa.deal (lockmethod) ؛ bussb.deal (lockmethod) ؛ Bussa.start () ؛ bussb.start () ؛ }}عند تشغيل البرنامج ، يمكنك أن ترى أنه أثناء تنفيذ BUSSA ، لا يمكن لـ BUSSB إدخال الوظيفة BUSSIB () ، لأنه يتم الحصول على قفل كائن LockMethod بواسطة BUSSA.
2. قلل من كتلة الكود المتزامن وقفل البيانات فقط
في بعض الأحيان لراحة البرمجة ، قام بعض الأشخاص بمزامنة قطعة كبيرة من الكود. إذا كانت بعض العمليات في كتلة الكود هذه غير مرتبطة بالموارد المشتركة ، فيجب وضعها خارج الكتلة المتزامنة لتجنب الإمساك بالأقفال لفترة طويلة ، مما تسبب في بقاء مؤشرات الترابط الأخرى في حالة انتظار. خاصة بعض عمليات الدورة وعمليات الإدخال/الإخراج المتزامنة. لا يعني ذلك فقط تقليل كتلة التزامن في نطاق الخط من الكود ، ولكن أيضًا في منطق التنفيذ ، يجب تقليل كتلة التزامن. على سبيل المثال ، أضف المزيد من الأحكام الشرطية ومزامنة ما إذا كانت تفي بالشروط ، بدلاً من إجراء أحكام مشروطة بعد المزامنة ، وذلك لتقليل المنطق غير الضروري الذي يدخل كتلة التزامن.
3. حاول ألا تتضمن أقفال في القفل
هذا الموقف يحدث في كثير من الأحيان. بعد الحصول على مؤشر الترابط القفل ، يستدعي طريقة التزامن لكائن آخر في كتلة طريقة التزامن ويحصل على القفل الثاني. قد يؤدي هذا إلى طلبات قفل متعددة في مكدس المكالمات. في حالة متعدد الخيوط ، قد يتسبب ذلك معقدًا للغاية ويصعب تحليل الاستثناءات ، مما يؤدي إلى طريق مسدود. يوضح الرمز التالي هذا:
متزامن (أ) {متزامن (ب) {}}أو تسمى طريقة التزامن في كتلة التزامن:
synchronized (a) {b b = objarraylist.get (0) ؛ B.Method () ؛ // هذه طريقة التزامن}الحل هو القفز وإضافة الأقفال ، ولا تتضمن الأقفال:
{b b = null ؛ synchronized (a) {b = objarraylist.get (0) ؛ } B.Method () ؛} 4. خصخصة القفل وإدارة القفل داخليًا
من الأكثر أمانًا استخدام القفل ككائن خاص ، ولا يمكن الحصول عليه من الخارج. قد يتم قفل كائن مباشرة بواسطة مؤشرات ترابط أخرى ، ويحمل مؤشر الترابط قفل كائن الكائن ، على سبيل المثال:
الفئة A {public void method1 () {}} class b {public void method1 () {a a = new a () ؛ Synchronized (a) {// lock مباشرة a.method1 () ؛ }}}في طريقة الاستخدام هذه ، يتم الاحتفاظ بقفل الكائن للكائن A من الخارج ، لذلك من الخطورة أن يتم استخدام القفل في أماكن متعددة في الخارج ، كما أنه يتسبب في مشاكل في قراءة التدفق المنطقي للرمز. طريقة أفضل هي إدارة الأقفال نفسها داخل الفصل وتوفير عمليات التزامن من خلال واجهات عندما تكون هناك حاجة إلى مخطط متزامن خارجي:
الفئة A {Private Object Lock = New Object () ؛ public void method1 () {synchronized (lock) {}}} class b {public void method1 () {a a = new a () ؛ A.Method1 () ؛ }} 5. إجراء تحلل القفل المناسب
النظر في الإجراء التالي:
الفئة العامة Gameserver {map public <string ، list <layer>> tables = new hashmap <string ، list <layer>> () ؛ public void join (player player ، table table) {if (player.getAccountbalance ()> table.getlimit ()) {synchronized (tables) {list <layer> tablePlayers = table.get (table.getid ()) ؛ if (tablePlayers.size () <9) {tablePlayers.add (player) ؛ }}}}} إجازة باطلة عامة (مشغل لاعب ، جدول الجدول) {/*shomit*/} public void createTable () {/*shomit*/} public void desterable (جدول الجدول) {/*shomit*/}}في هذا المثال ، لا تستخدم طريقة Join سوى قفل المزامنة واحدة فقط للحصول على كائن <layer> في الجداول ، ثم تحديد ما إذا كان عدد اللاعبين أقل من 9. إذا كان الأمر كذلك ، أضف لاعبًا واحدًا. عندما يكون هناك الآلاف من القائمة <layer> في الجداول ، ستكون المنافسة على أقفال الجداول شرسة للغاية. هنا ، يمكننا أن نفكر في تحلل القفل: بعد إخراج البيانات بسرعة ، قفل الكائن <player> ، حتى تتمكن مؤشرات الترابط الأخرى من التنافس بسرعة للحصول على قفل كائن الجداول:
فئة عامة Gameserver {
الخريطة العامة <string ،
قائمة <layer>> tables = new hashmap <string ،
قائمة <layer>> () ؛
انضمام باطل عام (لاعب لاعب ، جدول الجدول) {
if (player.getAccountbalance ()> table.getLimit ()) {
قائمة <layer> tablePlayers = null ؛
متزامن (الجداول) {
tablePlayers = tables.get (table.getId ()) ؛
}
متزامن (TablePlayers) {
if (tableplayers.size () <9) {
TablePlayers.Add (لاعب) ؛
}
}
}
}
إجازة باطلة عامة (لاعب لاعب ، جدول الجدول) {
/*تم حذفه*/
}
الفراغ العام createTable () {
/*تم حذفه*/
}
الفراغ العام تدمير (جدول الجدول) {
/*تم حذفه*/
}
}