ملخص
تعتمد هذه السلسلة على مسار تحسين الأرقام في الذهب ، ومن أجل التعلم بشكل أفضل ، تم إجراء سلسلة من السجلات. تقدم هذه المقالة أساسًا: 1.
1. أفكار وطرق تحسين القفل
تم ذكر مستوى التزامن في مقدمة [التزامن العالي Java 1].
بمجرد استخدام القفل ، فهذا يعني أن هذا يحظر ، وبالتالي فإن التزامن أقل قليلاً من الموقف الخالي من القفل.
يشير تحسين القفل المذكور هنا إلى كيفية منع الأداء من أن يصبح فقيرًا جدًا في حالة الحظر. ولكن بغض النظر عن كيفية تحسينه ، فإن الأداء سيكون أسوأ بشكل عام من الوضع الخالي من القفل.
تجدر الإشارة هنا إلى أن Trylock في REENTRANTLOCK المذكورة في [CONCRENCY JAVA V] JDK CONCERRENCY PACK 1 تميل إلى أن تكون طريقة خالية من القفل لأنها لن تعلق نفسها عندما يكون قضاة Trylock.
لتلخيص أفكار وطرق تحسين القفل ، هناك الأنواع التالية.
1.1 تقليل وقت القفل
syncmethod syncmethod () {othercode1 () ؛ mutextMethod () ؛ OtherCode2 () ؛ } مثل الرمز أعلاه ، تحتاج إلى الحصول على القفل قبل إدخال الطريقة ، ويجب أن تنتظر مؤشرات الترابط الأخرى في الخارج.
تتمثل نقطة التحسين هنا في تقليل وقت انتظار المواضيع الأخرى ، لذلك يتم استخدامها فقط لإضافة أقفال على البرامج ذات متطلبات سلامة الخيط.
public void syncmethod () {OtherCode1 () ؛ متزامن (هذا) {mutextMethod () ؛ } othercode2 () ؛ }1.2 قلل من حجم جسيم القفل
تقسيم الكائنات الكبيرة (يمكن الوصول إلى هذا الكائن من قبل العديد من مؤشرات الترابط) في كائنات صغيرة ، وزيادة التوازي بشكل كبير وتقليل منافسة القفل. فقط من خلال تقليل المنافسة على الأقفال والتحيز نحو الأقفال ، سيتم تحسين معدل نجاح الأقفال الخفيفة.
الحالات الأكثر نموذجية للحد من قفل الحبيبات هي concurrenthashmap. هذا مذكور في [حزمة التزامن العالية Java V] JDK Concurrency 1.
1.3 قفل فصل
إن فصل القفل الأكثر شيوعًا هو قفل القراءة القراءة للكتابة ، والذي يتم فصله إلى أقفال القراءة والكتابة وكتابة الأقفال وفقًا للوظيفة. وبهذه الطريقة ، لا تكون القراءة والقراءة حصرية بشكل متبادل ، فالقراءة والكتابة حصرية بشكل متبادل ، مما يضمن سلامة الخيوط ويحسن الأداء. للحصول على التفاصيل ، يرجى التحقق من [حزمة التزامن العالية Java V] JDK Concurrency Package 1.
يمكن تمديد فكرة فصل القراءة والكتابة ، وطالما لا تؤثر العمليات على بعضها البعض ، يمكن فصل القفل.
على سبيل المثال ، LinkedBlockingqueue
خذها من الرأس ووضع البيانات من الذيل. بالطبع ، يشبه أيضًا سرقة العمل في Forkjoinpool المذكورة في [Concrency High Java VI] JDK Concrency Package 2.
1.4 قفل خشن
بشكل عام ، من أجل ضمان التزامن الفعال بين مؤشرات الترابط المتعددة ، سيُطلب من كل مؤشر ترابط الاحتفاظ بالقفل بأقصى قدر ممكن ، أي أنه يجب إصدار القفل مباشرة بعد استخدام الموارد العامة. وبهذه الطريقة فقط ، يمكن لخيوط أخرى تنتظر هذا القفل الحصول على موارد لتنفيذ المهام في أقرب وقت ممكن. ومع ذلك ، كل شيء له درجة. إذا تم طلب نفس القفل باستمرار ومزامنة وإطلاق سراحه ، فسيستهلك موارد قيمة للنظام ، وهو ما لا يفضي إلى تحسين الأداء.
على سبيل المثال:
public void demomethod () {synchronized (lock) {// do sth. } // القيام بأعمال التزامن غير المرغوب فيها الأخرى ، ولكن يمكن تنفيذها بسرعة مزامنة (قفل) {// do sth. }} في هذه الحالة ، وفقًا لفكرة تخشير القفل ، يجب دمجها
public void demomethod () {// integrate في طلب قفل متزامن (قفل) {// do sth. // قم بعمل التزامن غير المرغوب فيه ، ولكن يمكن تنفيذه بسرعة}}} بالطبع ، هناك شرط أساسي ، سيتم تنفيذ العمل في الوسط الذي لا يتطلب التزامن بسرعة.
اسمحوا لي أن أقدم لك مثالًا متطرفًا آخر:
لـ (int i = 0 ؛ i <circle ؛ i ++) {synchronized (lock) {}} يجب الحصول على الأقفال في حلقة. على الرغم من أن JDK ستعمل على تحسين هذا الرمز داخليًا ، فمن الأفضل كتابته مباشرة
متزامن (قفل) {for (int i = 0 ؛ i <circle ؛ i ++) {}} بالطبع ، إذا كانت هناك حاجة للقول إن مثل هذه الحلقة طويلة جدًا وتحتاج إلى إعطاء مؤشرات ترابط أخرى حتى لا تنتظر طويلاً ، فيمكنك فقط كتابتها كما ورد أعلاه. إذا لم يكن هناك مثل هذه المتطلبات المماثلة ، فمن الأفضل كتابتها مباشرة إلى ما يلي.
1.5 القفل القضاء
القفل القفل هو شيء على مستوى المترجم.
في المترجم الفوري ، إذا تم العثور على الكائنات التي لا يمكن مشاركتها ، يمكن القضاء على تشغيل قفل هذه الكائنات.
ربما ستجد أنه من الغريب أنه نظرًا لأنه لا يمكن الوصول إلى بعض الكائنات بواسطة مؤشرات ترابط متعددة ، فلماذا يجب أن أضيف أقفال؟ ألن يكون من الأفضل عدم إضافة أقفال عند كتابة التعليمات البرمجية.
ولكن في بعض الأحيان ، لا يتم كتابة هذه الأقفال من قبل المبرمجين. لدى بعضها أقفال في تطبيقات JDK ، مثل فئات مثل Vector و StringBuffer. العديد من أساليبهم لها أقفال. عندما نستخدم طرق هذه الفئات دون سلامة الخيط ، عند استيفاء شروط معينة ، سيقوم برنامج التحويل البرمجي بإزالة القفل لتحسين الأداء.
على سبيل المثال:
رميات الفراغ الثابتة العامة (سلسلة args []) interruptedException {long start = system.currentTimeMillis () ؛ لـ (int i = 0 ؛ i <2000000 ؛ i ++) {createStringBuffer ("jvm" ، "التشخيص") ؛ } buffercost long = system.currentTimeMillis () - start ؛ System.out.println ("CraetestringBuffer:" + BufferCost + "MS") ؛ } سلسلة ثابتة عامة CreateStringBuffer (String S1 ، String S2) {StringBuffer SB = new StringBuffer () ؛ SB.Append (S1) ؛ SB.Append (S2) ؛ إرجاع sb.tostring () ؛ } stringbuffer.append في الكود أعلاه عبارة عن عملية متزامنة ، لكن StringBuffer هو متغير محلي ، والطريقة لا تُرجع stringbuffer ، لذلك من المستحيل على عدة مؤشرات ترابط الوصول إليها.
ثم عملية التزامن في StringBuffer لا معنى لها في هذا الوقت.
يتم تعيين إلغاء القفل على معلمات JVM ، بالطبع ، يجب أن يكون في وضع الخادم:
-Server -xx:+doSaPeanalysis -xx:+lextelocks
وتشغيل تحليل الهروب. تتمثل وظيفة تحليل الهروب في معرفة ما إذا كان من المحتمل أن يهرب المتغير من النطاق.
على سبيل المثال ، في StringBuffer أعلاه ، فإن إرجاع CraetestringBuffer في الكود أعلاه هو سلسلة ، لذلك لن يتم استخدام هذا المتغير المحلي في مكان آخر. إذا قمت بتغيير CraetestringBuffer
Static Static StringBuffer CraetestringBuffer (String S1 ، String S2) {StringBuffer SB = New StringBuffer () ؛ SB.Append (S1) ؛ SB.Append (S2) ؛ إرجاع SB ؛ } ثم بعد إرجاع هذا stringBuffer ، يمكن استخدامه في أي مكان آخر (على سبيل المثال ، ستعيد الوظيفة الرئيسية النتيجة وتضعها في الخريطة ، إلخ). ثم يمكن تحليل تحليل JVM Escape أن هذا المتغير المحلي stringBuffer يهرب من نطاقه.
لذلك ، استنادًا إلى تحليل الهروب ، يمكن لـ JVM الحكم على أنه إذا لم يهرب StringBuffer المتغير المحلي من نطاقه ، فيمكن تحديد أنه لن يتم الوصول إلى StringBuffer بواسطة مؤشرات ترابط متعددة ، ثم يمكن إزالة هذه الأقفال الإضافية لتحسين الأداء.
عندما تكون معلمات JVM:
-Server -xx:+doSaPeanalysis -xx:+lextelocks
الإخراج:
CraetestringBuffer: 302 مللي ثانية
معلمات JVM هي:
-Server -xx:+doSaPeanalysis -xx: -eleminatelocks
الإخراج:
CraetestringBuffer: 660 مللي ثانية
من الواضح أن تأثير التخلص من القفل لا يزال واضحًا جدًا.
2. قفل الأمثل في الجهاز الظاهري
أولاً ، نحتاج إلى تقديم رأس الكائن. في JVM ، يحتوي كل كائن على رأس كائن.
علامة علامة ، علامة لرأس الكائن ، 32 بت (نظام 32 بت).
صف التجزئة ، معلومات القفل ، علامات جمع القمامة ، العمر
كما سيحفظ مؤشرًا إلى سجل القفل ، ومؤشر على الشاشة ، ومعرف مؤشر ترابط القفل المتحيز ، إلخ.
ببساطة ، رأس الكائن هو حفظ بعض المعلومات المنهجية.
2.1 قفل إيجابي
ما يسمى بالتحيز هو غريب الأطوار ، أي أن القفل سوف يميل نحو الخيط الذي يمتلك القفل حاليًا.
في معظم الحالات ، لا توجد منافسة (في معظم الحالات ، لا تحتوي كتلة التزامن على مؤشرات ترابط متعددة في نفس قفل المنافسة في الوقت) ، لذلك يمكن تحسين الأداء عن طريق التحيز. هذا هو ، عندما لا تكون هناك منافسة ، عندما يحصل مؤشر الترابط الذي تم الحصول عليه مسبقًا على القفل مرة أخرى ، سيحدد ما إذا كان القفل يشير إلي ، لذلك لن يحتاج الخيط إلى الحصول على القفل مرة أخرى ويمكنه إدخال كتلة التزامن مباشرة.
يتم تنفيذ قفل التحيز هو تعيين علامة علامة رأس الكائن على أنها منحازة واكتب معرف مؤشر الترابط إلى علامة رأس الكائن.
عندما تطلب مؤشرات الترابط الأخرى نفس القفل ، ينتهي وضع التحيز
يتيح JVM قفل التحيز افتراضيًا -xx:+usebiasedlocking
في المنافسة الشرسة ، سيزيد القفل المتحيز من عبء النظام (تم إضافة ما إذا كان يتم تحيزه في كل مرة)
مثال على القفل المتحيز:
اختبار الحزمة ؛ استيراد java.util.list ؛ استيراد java.util.vector ؛ اختبار الفئة العامة {قائمة ثابتة عامة <integer> numberlist = new vector <integer> () ؛ الفراغ الثابت العام (سلسلة [] args) يلقي interruptedException {long begin = system.currentTimeMillis () ؛ عدد int = 0 ؛ int startNum = 0 ؛ بينما (العد <10000000) {numberlist.add (startNum) ؛ startNum += 2 ؛ count ++ ؛ } نهاية طويلة = system.currentTimeMillis () ؛ system.out.println (end - start) ؛ }} المتجه هو فئة آمنة مؤشرات الترابط تستخدم آلية القفل داخليًا. في كل مرة إضافة ، سيتم تقديم طلب قفل. يحتوي الرمز أعلاه على مؤشر ترابط واحد فقط ثم يضيف مرارًا وتكرارًا طلب القفل.
استخدم معلمات JVM التالية لتعيين قفل التحيز:
-xx:+usebiasedlocking -xx: piasedlockingStartupDelay = 0
BiasedLockingStartupDelay يعني أن قفل التحيز قد تم تمكينه بعد بدء النظام لبضع ثوان. الافتراضي هو 4 ثوان ، لأنه عندما يبدأ النظام ، تكون مسابقة البيانات العامة شرسة نسبيًا. سيؤدي أقفال التحيز الممكّنة في هذا الوقت إلى تقليل الأداء.
منذ هنا ، من أجل اختبار أداء قفل التحيز ، يتم تعيين وقت قفل تحيز التأخير على 0.
في هذا الوقت ، يكون الإخراج 9209
قم بإيقاف تشغيل قفل التحيز أدناه:
-xx: -usebiasedlocking
الإخراج هو 9627
بشكل عام ، عندما لا تكون هناك منافسة ، سيتم تحسين أداء أقفال التمكين للتمكين بحوالي 5 ٪.
2.2 قفل خفيف الوزن
يتم تنفيذ سلامة Java متعددة الخيوط بناءً على آلية القفل ، وغالبًا ما يكون أداء القفل غير مرضٍ.
والسبب هو أن المراقبة والرصد ، يتم تنفيذ اثنين من بدائيات Bytecode التي تتحكم في مزامنة Multithread ، بواسطة JVM تعتمد على Mutex لنظام التشغيل.
Mutex هي عملية تستهلك للموارد نسبيًا تتسبب في تعليق الخيط ويحتاج إلى إعادة جدولة مرة أخرى إلى الخيط الأصلي في فترة زمنية قصيرة.
من أجل تحسين آلية قفل Java ، تم تقديم مفهوم قفل الوزن الخفيف منذ Java6.
يهدف قفل الوزن الخفيف إلى تقليل فرصة إدخال MUTEREADENTING MUTEX ، وليس استبدال MUTEX.
يستخدم CPU Primistive Cart-and-SWAP (CAS ، تعليمات التجميع CMPXCHG) ، ويحاول علاج قبل الدخول إلى Mutex.
إذا فشل قفل التحيز ، فسيقوم النظام بإجراء عملية قفل خفيفة الوزن. الغرض من وجوده هو تجنب استخدام Mutex على مستوى نظام التشغيل قدر الإمكان ، لأن هذا الأداء سيكون ضعيفًا نسبيًا. نظرًا لأن JVM نفسه هو تطبيق ، آمل أن أحل مشكلة مزامنة مؤشر الترابط على مستوى التطبيق.
لتلخيص ، قفل خفيف الوزن هو طريقة قفل سريعة. قبل إدخال Mutex ، استخدم عمليات CAS لمحاولة إضافة أقفال. حاول عدم استخدام Mutex على مستوى نظام التشغيل لتحسين الأداء.
ثم عندما يفشل قفل التحيز ، فإن خطوات قفل الوزن الخفيف:
1. احفظ مؤشر علامة رأس الكائن في الكائن المقفل (يشير الكائن هنا إلى الكائن المقفل ، مثل المزامنة (هذا) {} ، هذا هو الكائن هنا).
lock-> set_displaced_header (mark) ؛
2. اضبط رأس الكائن كمؤشر على القفل (في مساحة مكدس الخيط).
if (mark == (markoop) Atomic :: cmpxchg_ptr (lock ، obj ()-> mark_addr () ، mark)) {tevent (slow_enter: relex stacklock) ؛ يعود ؛ } يقع القفل في مكدس الخيط. لذلك ، لتحديد ما إذا كان مؤشر ترابط يحمل هذا القفل ، فقط حدد ما إذا كانت المساحة التي يشير إليها رأس الكائن في مساحة عنوان مكدس مؤشر الترابط.
إذا فشل القفل خفيف الوزن ، فهذا يعني أن هناك منافسة وترقية إلى قفل الوزن الثقيل (قفل منتظم) ، وهو طريقة التزامن على مستوى نظام التشغيل. في حالة عدم وجود منافسة القفل ، تقلل الأقفال الخفيفة من فقدان الأداء الناجم عن الأقفال التقليدية باستخدام Mutexes OS. عندما تكون المنافسة شرسة للغاية (تفشل الأقفال الخفيفة دائمًا) ، تقوم الأقفال الخفيفة بالكثير من العمليات الإضافية ، مما يؤدي إلى تدهور الأداء.
2.3 قفل تدور
عند وجود المنافسة ، نظرًا لأن محاولة قفل الوزن الخفيف ، قد يتم ترقيتها مباشرة إلى قفل الوزن الثقيل لاستخدام الإقصاء المتبادل على مستوى نظام التشغيل. من الممكن أيضًا تجربة قفل الدوران مرة أخرى.
إذا كان بإمكان مؤشر الترابط الحصول على القفل بسرعة ، فلا يمكنك تعليق الخيط في طبقة نظام التشغيل ، والسماح للمعلومات بعمل عدة عمليات فارغة (تدور) ، وحاول باستمرار الحصول على القفل (على غرار Trylock). بالطبع ، عدد الحلقات محدود. عندما يصل عدد الحلقات ، سيظل ترقيته إلى قفل الوزن الثقيل. لذلك ، عندما لا يكون لكل موضوع سوى القليل من الوقت لعقد القفل ، يمكن أن يحاول قفل الدوران تجنب خيوط تعليقها في طبقة OS.
JDK1.6 -xx:+تم تمكين USESPINNING
في JDK1.7 ، قم بإزالة هذه المعلمة وقم بتغييرها إلى تطبيق مدمج.
إذا كانت كتلة التزامن طويلة جدًا وفشل الدوران ، فسيتم تدهور أداء النظام. إذا كانت كتلة التزامن قصيرة جدًا ونجاح الدوران ، فإنه يحفظ وقت تبديل التعليق الخيط ويحسن أداء النظام.
2.4 قفل إيجابي ، قفل خفيف الوزن ، ملخص قفل الدوران
القفل أعلاه ليس طريقة تحسين قفل لغة Java ، ولكنه مدمج في JVM.
بادئ ذي بدء ، يتمثل أقفال التحيز في تجنب استهلاك أداء مؤشر ترابط عندما يكتسب/يطلق القفل نفسه بشكل متكرر. إذا كان الخيط نفسه لا يزال يحصل على هذا القفل ، فسيقوم بإدخال كتلة التزامن مباشرة عند محاولة تحيز الأقفال ، وليس هناك حاجة للحصول على القفل مرة أخرى.
تهدف أقفال وخفيفة الوزن وأقفال الدوران إلى تجنب المكالمات المباشرة لعمليات Mutex على مستوى نظام التشغيل ، لأن تعليق المواضيع هي عملية تستغرق الموارد للغاية.
لتجنب استخدام أقفال الوزن الثقيل (Mutex على مستوى نظام التشغيل) ، سنحاول أولاً قفلًا خفيف الوزن. سيحاول القفل خفيف الوزن استخدام عملية CAS للحصول على القفل. إذا فشل قفل الوزن الخفيف في الحصول عليه ، فهذا يعني أن هناك منافسة. ولكن ربما ستحصل على القفل قريبًا ، وسوف تحاول أن تقوم بأقفال الدوران ، وتفعل بعض الحلقات الفارغة على الخيط ، وتحاول الحصول على الأقفال في كل مرة تقوم فيها بالحلقة. إذا فشل قفل الدوران أيضًا ، فيمكنه ترقيته فقط إلى قفل الوزن الثقيل.
يمكن ملاحظة أن الأقفال المتحيزة ، والأقفال الخفيفة ، والأقفال الدورانية كلها أقفال متفائلة.
3. حالة استخدام الأقفال بشكل غير صحيح
الطبقة العامة integerlock {static integer i = 0 ؛ يمتد AddThread الفئة الثابتة العامة thread {public void run () {for (int k = 0 ؛ k <100000 ؛ k ++) {synchronized (i) {i ++ ؛ }}}} public static void main (string [] args) remrows interruptedException {addThread t1 = new AddThread () ؛ AddThread T2 = جديد AddThread () ؛ t1.start () ؛ t2.start () ؛ t1.join () ؛ t2.join () ؛ system.out.println (i) ؛ }} هناك خطأ أساسي للغاية هو أنه في نمط تصميم التزامن [Concrency High Java VII] ، يكون Interger غير متغير نهائي ، وبعد كل ++ ، سيتم إنشاء interger جديد وتعيينه إلى I ، وبالتالي تختلف الأقفال بين الخيطين. لذلك ليس آمن الخيط.
4. Threadlocal وتحليل رمز المصدر
قد يكون من غير المناسب بعض الشيء ذكر ThreadLocal هنا ، ولكن Threadlocal هو وسيلة لاستبدال الأقفال. لذلك لا يزال من الضروري ذكر ذلك.
الفكرة الأساسية هي أنه في سلسلة متعددة ، يجب قفل البيانات. إذا تم استخدام ThreadLocal ، يتم توفير مثيل كائن لكل مؤشر ترابط. مؤشرات ترابط مختلفة فقط تصل إلى كائناتها الخاصة ، وليس الكائنات الأخرى. بهذه الطريقة ليست هناك حاجة لوجود القفل.
اختبار الحزمة ؛ استيراد java.text.parseException ؛ استيراد java.text.simpleDateFormat ؛ استيراد java.util.date ؛ استيراد java.util.concurrent.executorservice ؛ استيراد java.util.concurrent.executors ؛ HH: MM: SS ") ؛ Public Static Class Parsedate تنفذ Runnable {int i = 0 ؛ parsedate العامة (int i) {this.i = i ؛ } public void run () {try {date t = sdf.parse ("2016-02-16 17:00:" + i ٪ 60) ؛ System.out.println (i + ":" + t) ؛ } catch (parseException e) {E.PrintStackTrace () ؛ }}} public static void main (string [] args) {ExecutorService es = Executors.NewFixedThreadPool (10) ؛ لـ (int i = 0 ؛ i <1000 ؛ i ++) {es.execute (new parsedate (i)) ؛ }}} نظرًا لأن SimpleDateFormat ليس آمنًا لخيط الخيط ، يتم استخدام الكود أعلاه بشكل غير صحيح. أسهل طريقة هي تحديد فئة بنفسك ولفها مع متزامن (على غرار Collections.synchronizedMap). سيؤدي ذلك إلى حدوث مشاكل عند القيام بذلك بتزامن كبير. ينتج عن الخلاف على متزامن سلسلة واحدة فقط يدخل في وقت واحد ، وحجم التزامن منخفض للغاية.
يتم حل هذه المشكلة باستخدام threadlocal لتغليف SimpledAteFormat.
اختبار الحزمة ؛ استيراد java.text.parseException ؛ استيراد java.text.simpleDateFormat ؛ استيراد java.util.date ؛ استيراد java.util.concurrent.executorservice ؛ import java.util.concurrent.executors ؛ اختبار الفئة العامة Public Static Class Parsedate تنفذ Runnable {int i = 0 ؛ parsedate العامة (int i) {this.i = i ؛ } public void run () {try {if (tl.get () == null) {tl.set (new SimplEdateFormat ("Yyyy-MM-DD HH: mm: ss") ؛ } التاريخ t = tl.get (). parse ("2016-02-16 17:00:" + i ٪ 60) ؛ System.out.println (i + ":" + t) ؛ } catch (parseException e) {E.PrintStackTrace () ؛ }}} public static void main (string [] args) {ExecutorService es = Executors.NewFixedThreadPool (10) ؛ لـ (int i = 0 ؛ i <1000 ؛ i ++) {es.execute (new parsedate (i)) ؛ }}}عند تشغيل كل مؤشر ترابط ، سيحدد ما إذا كان مؤشر الترابط الحالي يحتوي على كائن SimpleDateFormat.
if (tl.get () == null)
إذا لم يكن الأمر كذلك ، فسيتم ربط SimpleDateFormat الجديد بالخيط الحالي
tl.set (جديد simpleatformat ("yyyy-mm-dd hh: mm: ss")) ؛
ثم استخدم SimpleDateFormat من الخيط الحالي لتحليله
tl.get (). parse ("2016-02-16 17:00:" + i ٪ 60) ؛
في الكود الأولي ، لم يكن هناك سوى واحد مبسط ، والذي استخدم threadlocal ، وكان التبسيط جديدًا لكل مؤشر ترابط.
تجدر الإشارة إلى أنه يجب ألا تقوم بتعيين simpledateformat العامة على كل مؤشر ترابط هنا ، لأن هذا عديم الفائدة. يجب إعطاء كل منها جديدًا إلى SimpleDateFormat.
في السبات ، هناك تطبيقات نموذجية لـ threadlocal.
دعونا نلقي نظرة على تنفيذ رمز المصدر لـ ThreadLocal
بادئ ذي بدء ، هناك متغير عضو في فئة الخيط:
threadlocal.throadlocalmap threadlocals = null ؛
وهذه الخريطة هي مفتاح تنفيذ threadlocal
مجموعة void العامة (t value) {thread t = thread.currentThRead () ؛ threadlocalmap خريطة = getMap (t) ؛ if (map! = null) map.set (هذا ، القيمة) ؛ Else CreateMap (t ، القيمة) ؛ } وفقًا لـ ThreadLocal ، يمكنك ضبط القيمة المقابلة والحصول عليها.
يشبه تطبيق ThreadLocalmap هنا HashMap ، ولكن هناك اختلافات في التعامل مع تعارضات التجزئة.
عندما يحدث تعارض التجزئة في Threadlocalmap ، لا يشبه HashMap استخدام القوائم المرتبطة لحل الصراع ، ولكن وضع الفهرس ++ في الفهرس التالي لحل الصراع.