في الآونة الأخيرة ، قرأت رمز طبقة إطار عمل Android ورأيت فئة ThreadLocal. كنت غير مألوف بعض الشيء ، لذلك قرأت العديد من المدونات ذات الصلة واحدة تلو الأخرى. ثم درست رمز المصدر ووجدت أن فهمي كان مختلفًا عن منشورات المدونة التي قرأتها من قبل ، لذلك قررت أن أكتب مقالًا للحديث عن فهمي ، على أمل أن يلعب الدور التالي:
- يمكن أن تطهير نتائج البحث وتعميق فهمك ؛
- يمكن أن يلعب دورًا في جذب اليشم ومساعدة الطلاب المهتمين على إزالة أفكارهم ؛
- شارك تجربتك التعليمية والتواصل والتعلم مع الجميع.
1. ما هو threadlocal
ThreadLocal هي الفئة الأساسية لمكتبة فئة Java ، تحت الحزمة java.lang ؛
التفسير الرسمي على النحو التالي:
ينفذ تخزين مؤشر ترابط محلي ، أي متغير له قيمته الخاصة. تشترك جميع مؤشرات الترابط في نفس كائن Threadlocal ، لكن كل منها يرى قيمة مختلفة عند الوصول إليها ، والتغييرات التي أجراها مؤشر ترابط واحد لا تؤثر على مؤشرات الترابط الأخرى. يدعم التنفيذ القيم الخالية.
المعنى العام هو:
يمكن تنفيذ آلية التخزين المحلية للمواضيع. متغير ThreadLocal هو متغير يمكن أن يكون له قيم مختلفة في مؤشرات الترابط المختلفة. يمكن لجميع مؤشرات الترابط مشاركة نفس كائن Threadlocal ، ولكن يمكن أن تحصل مؤشرات الترابط المختلفة على قيم مختلفة عند الوصول إليها ، ولن تؤثر تغييرات أي مؤشر ترابط إليه على مؤشرات الترابط الأخرى. تدعم تطبيقات الفصل القيم الفارغة (يمكن تمرير القيم الخالية والوصول إليها في SET والحصول على الأساليب).
باختصار ، هناك ثلاث خصائص:
- احصل على قيم مختلفة عند الوصول إليها بواسطة مؤشرات ترابط مختلفة
- لن يؤثر أي تغيير على موضوعه
- دعم فارغة
فيما يلي أمثلة على هذه الميزات. أولاً ، نحدد فئة اختبار. في هذا الفصل ، نتحقق من الميزات الثلاث المذكورة أعلاه. تعريف الفصل هو كما يلي:
test.java
اختبار الفئة العامة {// تعريف threadlocal private static static name ؛ public static void main (string [] args) يرمي الاستثناء {name = new threadlocal () ؛ // define thread athread a = new thread () a ") ؛ system.out.println (" بعد مجموعة الاستدعاء ، القيمة هي: "+name.get ()) ؛}} ؛ : "+name.get ()) ؛}} ؛ // not invoke set ، اطبع القيمة هي nullsystem.out.println (name.get ()) ؛ // invoke تعيين لملء valuename.set (" thread main ") ؛ // start thread aa.start () ؛ bb.start () ؛تحليل الكود:
من التعريف ، يمكننا أن نرى أنه يتم الإعلان عن كائن واحد فقط من مؤشر ترابط واحد ، وشرط مؤشرات الترابط الثلاثة الأخرى (مؤشر الترابط الرئيسي ، مؤشر الترابط A وخيط ب) نفس الكائن ؛ بعد ذلك ، يتم تعديل قيمة الكائن في مؤشرات ترابط مختلفة ويتم الوصول إلى قيمة الكائن في مؤشرات ترابط مختلفة ، والنتيجة هي الإخراج لعرضها في وحدة التحكم.
انظر النتائج:
من نتائج إخراج وحدة التحكم ، يمكنك أن ترى أن هناك ثلاثة مخرجات فارغة فيه. وذلك لأن الكائن لم يتم تعيينه قبل الإخراج ، والذي تحقق من ميزة دعم NULL. علاوة على ذلك ، يمكن العثور على أنه في كل مؤشر ترابط ، قمت بتعديل قيمة الكائن ، ولكن عندما تصل مؤشرات الترابط الأخرى إلى الكائن ، فهي ليست القيمة المعدلة ، ولكن القيمة المحلية الخيط ؛ هذا يتحقق أيضا من الميزتين الأخريين.
2. دور threadlocal
يعلم الجميع أن سيناريوهات استخدامها هي في الغالب برمجة متعددة الخيوط. أما بالنسبة لوظائفها المحددة ، كيف تقول ذلك؟ أعتقد أن هذا لا يمكن تعريفه إلا بطريقة عامة ، لأن السمات الوظيفية لشيء ما سوف تحد من تفكير الجميع بعد تعريفه. على سبيل المثال ، يتم استخدام سكاكين المطبخ لقطع الخضار ، ولن يستخدمها الكثير من الناس لقطع البطيخ.
هنا ، سأتحدث عن فهمي لوظائفها للرجوع إليها فقط وآمل أن تكون مفيدة. دعونا نصفه بهذه الطريقة. عندما يحتاج برنامج متعدد الخيوط إلى تغليف بعض المهام لمعظم مؤشرات الترابط (أي بعض التعليمات البرمجية في طريقة التشغيل) ، يمكن استخدام ThreadLocal لالتفاف متغيرات الأعضاء المرتبطة بالموضوع في هيئة التغليف لضمان حصرية الوصول إلى مؤشرات الترابط ، ويمكن لجميع مؤشرات الترابط مشاركة كائن تغليف ؛ يمكنك الرجوع إلى Looper في Android. المبرمجين الذين لا يستطيعون وصف مشاكل الكود ليسوا مبرمجين جيدين ؛
انظر إلى الكود: أداة لتوضيح رمز الاستهلاك للوقت لخيط (تم إنشاؤه لتوضيح المشكلة)
StatisticCosttime.java
. مثيل actactory.instance ؛} مثيل فئة ثابتة خاصة {private static static final statistictime extal = new StatisticCosttime () ؛} // startPublic void start () {// StartTime.set (system. startTime.get ()) ؛ costtime = system.nanotime () - startTime ؛} public getStartTime () {return starttime ؛ // return startTime.get () ؛} public getCosttime () {// costtime.get () ؛حسنًا ، تم الانتهاء من تصميم الأدوات ، والآن نستخدمه لحساب المواضيع المستهلكة للوقت ومحاولة:
main.java
الطبقة العامة الرئيسية {public static void main (string [] args) يلقي الاستثناء {// تحديد مؤشر ترابط athread a = new thread () {public void run () {try {// start record record timestatistictime.shareinstance (). System.out.println ("A-StartTime:"+StatisticCosttime.ShareInstance (). getStartTime ()) ؛ // إنهاء recordstatisticcosttime.shareinstance (). e) {}}} ؛ // ابدأ aa.start () ؛ // تحديد مؤشر الترابط bthread b = new thread () {public void run () {try {// سجل وقت بدء تشغيل وقت البدء إلى B1statistictTime.ShareInstance (). consomesystem.out.println ("B1-StartTime:"+StatisticCosttime.ShareInstance (). b1system.out.println ("B1:"+StatisticCosttime.ShareInstance (). b2system.out.println ("B2-StartTime:"+StatisticCosttime.ShareInstance (). b2system.out.println ("B2:"+statisticCosttime.ShareInstance ().بعد تشغيل الكود ، تكون نتيجة الإخراج كما يلي: دقة نتيجة الإخراج هي النانو ثانية.
يعتمد ذلك على ما إذا كانت النتيجة مختلفة عما توقعناه. لقد وجدت أن نتيجة A يجب أن تكون مساوية تقريبًا لـ B1+B2. كيف يصبح مثل B2؟ الجواب هو أنه عندما نحدد متغيرات وقت البدء و COSTTIME ، يجب عدم مشاركة النية الأصلية ، ولكن يجب أن تكون حصرية للخيط. هنا تتم مشاركة المتغيرات مع Singleton ، لذلك عند حساب قيمة A ، تم تعديل وقت البدء بالفعل بواسطة B2 ، وبالتالي فإن النتيجة نفسها مثل الإخراج B2.
الآن دعنا نفتح الجزء الذي تم التعليق عليه في StatisticCosttime وتجربته عن طريق تغييره إلى طريقة إعلان Threadlocal.
انظر النتائج:
آه! هذا حقق التأثير المتوقع. في هذا الوقت ، قد يقول بعض الطلاب أن هذا ليس وصولًا للمتأشرين ، وهل يمكنني التأكد من سلامة مؤشرات الترابط طالما أستخدم ThreadLocal؟ الجواب لا! بادئ ذي بدء ، يمكننا معرفة سبب وجود مشكلات في سلامة الخيوط ، ولكن هناك حالتان فقط:
1. لديك موارد مشتركة لا ينبغي مشاركتها بين المواضيع ؛
2. لا تضمن الوصول المنظم إلى الموارد المشتركة بين المواضيع ؛
يمكن حل الأول بواسطة طريقة "وقت التبادل المكاني" ، باستخدام ThreadLocal (يمكنك أيضًا إعلان المتغيرات المحلية لخيط الخيط بشكل مباشر) ، ويمكن حل هذا الأخير بواسطة طريقة "وقت التبادل المكاني" ، وهو ما لا يمكن أن يفعله ThreadLocal.
3. مبدأ Threadlocal
مبدأ التنفيذ بسيط للغاية. في كل مرة تكون عملية القراءة والكتابة إلى كائن Threadlocal هي في الواقع عملية قراءة وتكتب إلى كائن قيم مؤشر الترابط ؛ نوضح هنا أنه لا يوجد إنشاء نسخة من المتغير ، لأن مساحة الذاكرة المخصصة بواسطة المتغير لا تستخدم لتخزين كائن T ، ولكن يتم استخدام قيم مؤشر الترابط لتخزين كائن T ؛ في كل مرة نسميها طريقة مجموعة ThreadLocal في مؤشر الترابط ، فهي في الواقع عملية لكتابة الكائن إلى كائن القيم المقابلة للمعلومات ؛ عند استدعاء طريقة GET ThreadLocal ، فهي في الواقع عملية للحصول على الكائن من كائن القيم المقابلة للمعلومات.
انظر رمز المصدر:
مجموعة متغير الأعضاء Threadlocal
/*** يعين قيمة هذا المتغير للمعلومات الحالية. إذا تم تعيينه على * {code null} ، فسيتم ضبط القيمة على NULL وستظل الإدخال الأساسي موجودًا. * * @param قيمة القيمة الجديدة للمتغير لخيط المتصل. */مجموعة void العامة (t value) {thread currentThread = thread.currentThread () ؛ القيم القيم = القيم (CurrentThread) ؛ if (القيم == null) {values = initializeValues (currentThread) ؛ } القيم.طريقة الأعضاء treadlocal get
/*** إرجاع قيمة هذا المتغير للخيط الحالي. إذا لم يكن إدخال * موجودًا بعد لهذا المتغير في هذا الموضوع ، فستقوم هذه الطريقة * بإنشاء إدخال ، يملأ القيمة بنتيجة * {link #InitialValue ()}. * * @إعادة القيمة الحالية للمتغير لخيط الاتصال. */@CrespressWarnings ("Unchecked") public t get () {// محسّن للمسار السريع. thread currentThRead = thread.currentThRead () ؛ القيم القيم = القيم (CurrentThread) ؛ if (القيم! = null) {object [] table = value.table ؛ int index = hash & stable.mask ؛ if (this.reference == table [index]) {return (t) table [index + 1] ؛ }} else {values = initializeValues (currentThread) ؛ } إرجاع (t) القيم.طريقة الأعضاء threadlocal تهيئة القياس
/*** ينشئ مثيل القيم لهذا الموضوع والنوع المتغير. */القيم تهيئة القياس (مؤشر الترابط الحالي) {return current.localvalues = new Date () ؛}قيم طريقة الأعضاء threadlocal
/*** يحصل على مثيل القيم لهذا الموضوع والنوع المتغير. */القيم القيم (مؤشر الترابط الحالي) {return current.localvalues ؛}فكيف تقرأ وكتابة الكائنات في هذه القيم؟
توجد القيم كفئة داخلية من threadlocal ؛ تتضمن هذه القيم كائن صفيف مهم [] ، وهو الجزء الرئيسي من الإجابة على السؤال. يتم استخدامه لتخزين أنواع مختلفة من متغيرات treadlocal في الخيط. لذا فإن السؤال هو ، كيف يمكنك التأكد من أنك لا تحصل على قيم الأنواع الأخرى عند أخذ متغيرات من نوع معين؟ بشكل عام ، سيتم تعيين الخريطة وفقًا لقيمة المفتاح ؛ نعم ، الفكرة هي هذه الفكرة ، لكنها لم يتم تنفيذها باستخدام الخريطة هنا ، فهي آلية خريطة يتم تنفيذها باستخدام كائن [] ؛ ومع ذلك ، إذا كنت ترغب في استخدام الخريطة لفهمها ، فهذا غير ممكن لأن الآلية هي نفسها ؛ يتوافق المفتاح في الواقع مع المرجع الضعيف لـ Threadlocal ، والقيمة تتوافق مع الكائن الذي مررنا به.
دعنا نوضح كيفية استخدام الكائن [] لتنفيذ آلية الخريطة (راجع الشكل 1) ؛ يستخدم التكافؤ في مجموعة المصفوفة لتمييز المفتاح والقيمة ، أي أن الجدول أدناه يخزن المفتاح في الموضع الزوجي ويخزن الرقم الفردي القيمة. هذه هي الطريقة التي يتم بها. إذا كان الطلاب المهتمين يرغبون في معرفة تطبيق الخوارزمية ، فيمكنهم دراسته بعمق ، فلن أشرح ذلك بالتفصيل هنا.
بناءً على المثال الأول السابق ، يتم تحليل حالة التخزين:
عند تنفيذ البرنامج ، هناك ثلاثة مؤشرات ترابط A و B و Main. عندما يتم استدعاء name.set () في مؤشر الترابط ، يتم تخصيص ثلاثة مساحات ذاكرة متطابقة في منطقة الكومة لثلاث مثيلات مؤشر ترابط في نفس الوقت لتخزين كائن القيم ، باستخدام مرجع الاسم مثل المفتاح ، ويتم تخزين الكائن المحدد كقيمة في ثلاثة كائنات مختلفة [] (انظر الشكل أدناه):
4. ملخص
لا يمكن لـ ThreadLocal حل مشكلة التزامن تمامًا أثناء البرمجة متعددة الخيوط. تتطلب هذه المشكلة أيضًا حلولًا مختلفة للاختيار وفقًا لمواقف مختلفة ، "Special for Time" أو "وقت للمكان".
تتمثل الدالة الأكبر في ThreadLocal في تحويل متغيرات المشاركة في مؤشر الترابط إلى متغيرات محلية مؤشر ترابط لتحقيق العزلة بين الخيوط.
ما سبق هو كل شيء عن فهم Threadlocal بسرعة في جافا. آمل أن يكون ذلك مفيدًا للجميع. إذا كانت هناك أي أوجه قصور ، فيرجى ترك رسالة لإشارةها. شكرا لك يا أصدقائك لدعمكم لهذا الموقع.