مقدمة
مشكلة سلامة مؤشرات الترابط في Multithreading خفية وغير متوقعة ، لأن ترتيب العمليات في multithreads لا يمكن التنبؤ بها دون التزامن المناسب. مؤشرات ترابط متعددة تصل إلى نفس المتغير المشترك معرضة بشكل خاص لمشاكل التزامن ، خاصةً عندما تحتاج مؤشرات ترابط متعددة إلى كتابة متغير مشترك ، من أجل ضمان سلامة مؤشرات الترابط ،
بشكل عام ، يحتاج المستخدمون إلى تنفيذ التزامن المناسب عند الوصول إلى المتغيرات المشتركة ، كما هو موضح في الشكل أدناه:
يمكن ملاحظة أن مقياس التزامن هو قفل عمومًا ، مما يتطلب من المستخدم أن يكون لديه فهم معين للقفل ، مما يزيد من العبء على المستخدم. فهل هناك طريقة عند إنشاء متغير ، عندما يصل كل مؤشر ترابط إلى ذلك ، فإنه يصل إلى متغيرات مؤشر ترابطه؟ في الواقع ، يمكن أن يفعل Threalocal هذا. لاحظ أن ظهور ThreadLocal لا يبدو أنه يحل المشكلات المذكورة أعلاه.
يتم توفير ThreadLocal في حزمة JDK. يوفر متغيرات المحلية الخيط. وهذا هو ، إذا قمت بإنشاء متغير مؤشر ترابط ، فإن كل مؤشر ترابط يصل إلى هذا المتغير سيكون له نسخة محلية من المتغير. عندما تعمل مؤشرات ترابط متعددة على هذا المتغير ، فإنها تعمل بالفعل على المتغيرات في ذاكرتها المحلية ، وبالتالي تجنب مشكلات سلامة مؤشرات الترابط. بعد إنشاء متغير threadlocal ،
سيقوم كل مؤشر ترابط بنسخ متغير إلى ذاكرته المحلية ، كما هو موضح في الشكل أدناه:
حسنًا ، دعونا الآن نفكر في سؤال: مبدأ التنفيذ لـ ThreadLocal ، وكيف يتم تنفيذ ThreadLocal كطريقة عزل مؤشر ترابط متغيرة ، داخليًا؟
أولاً ، نحتاج إلى إلقاء نظرة على بنية مخطط الفئة من threadlocal ، كما هو موضح في الشكل التالي:
يحب
كما يتضح من الرسم البياني للفئة أعلاه ، هناك عزلات ترابط و merinitablethreadlocals في فئة الخيط. كلا النوعين من المتغيرات هما threadlocalmap ، و threadlocalmap هو hashmap مخصصة. بشكل افتراضي ، كلا المتغيرين في كل مؤشر ترابط خالية. سيتم إنشاؤها فقط عندما يقوم مؤشر الترابط بتخصيص مجموعة threadlocal أو الحصول على الطريقة لأول مرة.
في الواقع ، لا يتم تخزين المتغيرات المحلية لكل مؤشر ترابط في مثيل Threadlocal ، ولكن يتم تخزينها في متغير Threadlocals من مؤشر ترابط الاتصال. بمعنى آخر ، يتم تخزين المتغيرات المحلية من Type Threadlocal في مساحة ذاكرة الخيط المحددة.
Threadlocal هو في الواقع قذيفة. يضع قيمة القيمة في مؤشر ترابط مؤشر ترابط الاتصال من خلال طريقة SET ويخزنها. عندما يستدعي مؤشر ترابط الاتصال طريقة GET ، يتم إخراجها من متغير Threadlocals من الخيط الحالي. إذا لم ينتهي مؤشر ترابط الاتصال ، فسيتم تخزين المتغير المحلي في متغير ThreadLocals من مؤشر ترابط الاتصال.
لذلك ، عندما لا تحتاج إلى استخدام المتغيرات المحلية ، يمكنك حذف المتغير المحلي من متغير ThreadLocals من مؤشر الترابط الحالي عن طريق استدعاء طريقة إزالة المتغير الخيط. قد يسأل بعض الأشخاص لماذا تم تصميم Threadlocals كهيكل خريطة؟ من الواضح أن كل مؤشر ترابط يمكن أن يرتبط بمتغيرات Threadlocal متعددة.
بعد ذلك ، يمكننا إدخال الكود المصدر في ThreadLocal كما هو موضح في الكود التالي:
يبحث بشكل أساسي عن منطق تنفيذ الأساليب الثلاثة التي تم تعيينها ، والحصول عليها ، وإزالتها ، على النحو التالي:
دعونا نلقي نظرة على طريقة المجموعة (t var1) أولاً
مجموعة void العامة (t var1) {// (1) احصل على مؤشر ترابط مؤشر الترابط الحالي var2 = thread.currentThread () ؛ // (2) يتم استخدام مؤشر الترابط الحالي كمفتاح للعثور على متغير مؤشر الترابط المقابل. إذا وجدت ، تعيين threadlocal.throadlocalmap var3 = this.getMap (var2) ؛ if (var3! = null) {var3.set (this ، var1) ؛ } آخر {// (3) يتم إنشاء المكالمة الأولى لإنشاء hashMAP المقابل للمعلومات الحالية this.createMap (var2 ، var1) ؛ }}كما هو مذكور أعلاه الرمز (1) ، احصل أولاً على مؤشر ترابط الاتصال ، ثم استخدم مؤشر الترابط الحالي كمعلمة للاتصال بالطريقة GETMAP (var2). رمز GetMap (Thread Var2) هو كما يلي:
threadlocal.ThreadLocalmap getMap (thread var1) {return var1.throadlocals ؛ }يمكن ملاحظة أن ما يفعله GetMap (VAR2) هو الحصول على متغيرات المتغير الخاصة بالمعلومات ، ومتغير ThreadLocal مرتبط بمتغير عضو مؤشر الترابط.
إذا كانت GetMap (var2) لا تفرغ ، فقم بتعيين قيمة القيمة على phindlocals ، أي وضع القيمة المتغيرة الحالية في متغير الذاكرة المتغير من مؤشر الترابط الحالي. Threadlocals هي بنية hashmap ، حيث يكون المفتاح مرجع كائن مثيل مؤشر الترابط الحالي ، والقيمة هي القيمة التي تم تمريرها من خلال طريقة المجموعة.
إذا تم إرجاع GetMap (var2) فارغًا ، فهذا يعني أن طريقة SET تسمى المرة الأولى ، ثم يتم إنشاء متغير ThreadLocals للمعلومات الحالية. دعونا نرى ما الذي يتم في CreateMap (var2 ، var1)؟
void createMap (thread var1 ، t var2) {var1.throadlocals = new threadlocal.throadlocalmap (this ، var2) ؛ }ما يمكنك رؤيته هو إنشاء متغير Threadlocals من الخيط الحالي.
بعد ذلك ، دعونا نلقي نظرة على طريقة GET () ، الرمز كما يلي:
public t get () {// (4) احصل على مؤشر الترابط الحالي var1 = thread.currentThRead () ؛ // (5) احصل على threadlocals المتغير threadlocal.ThreadLocalmap var2 = this.getMap (var1) ؛ // (6) إذا لم يكن threadlocals فارغًا ، فسيتم إرجاع القيمة المتغيرة المحلية المقابلة إذا (var2! = null) {threadlocal.throadlocalmap.entry var3 = var2.getentry (this) ؛ if (var3! = null) {object var4 = var3.value ؛ إرجاع var4 ؛ }} // (7) إذا كان Threadlocals فارغًا ، يتم تهيئة متغير عضو مؤشر الترابط في مؤشر الترابط الحالي. إرجاع this.setInitialValue () ؛ }الرمز (4) أولاً احصل على مثيل الموضوع الحالي. إذا لم يكن متغير ThreadLocals من مؤشر الترابط الحالي فارغًا ، فسيعود مباشرة إلى المتغير المحلي للمعلومات الحالية. خلاف ذلك ، تنفيذ الرمز (7) لتهيئة ، ورمز setInitialValue () كما يلي:
private t setInitialValue () {// (8) تهيئة إلى كائن فارغ var1 = this.initialValue () ؛ الموضوع var2 = thread.currentThRead () ؛ threadlocal.ThreadLocalmap var3 = this.getMap (var2) ؛ // (9) إذا كان متغير threadlocals من متغير مؤشر الترابط الحالي غير فارغ إذا (var3! = null) {var3.set (this ، var1) ؛ // (10) إذا كان متغير ThreadLocals من مؤشر الترابط الحالي فارغًا} آخر {this.createMap (var2 ، var1) ؛ } إرجاع var1 ؛ }كما هو مذكور أعلاه ، إذا لم يكن متغير ThreadLocals من مؤشر الترابط الحالي فارغًا ، فسيتم ضبط القيمة المتغيرة المحلية للمعلومات الحالية على NULL. خلاف ذلك ، يتم استدعاء CreateMap إلى متغير CreateMap للخيط الحالي.
بعد ذلك ، ننظر إلى طريقة إزالة الفراغ () ، الرمز كما يلي:
public void remove () {threadlocal.threadlocalmap var1 = this.getMap (thread.currentThread ()) ؛ if (var1! = null) {var1.remove (this) ؛ }}كما هو مذكور أعلاه ، إذا لم يكن متغير ThreadLocals من مؤشر الترابط الحالي فارغًا ، فسيتم حذف المتغير المحلي المحدد في مثيل Threadlocal في مؤشر الترابط الحالي.
بعد ذلك ، دعنا نلقي نظرة على العرض التوضيحي المحدد ، الرمز هو كما يلي:
/*** تم إنشاؤه بواسطة Cong في 2018/6/3. */public class threadlocaltest {// (1) print print static void print (string str) {//1.1 اطبع قيمة المتغير المحلي في الذاكرة المحلية لنظام الخيط الحالي. //1.2 امسح المتغير المحلي في الذاكرة المحلية للمعلومات الحالية //localvariable.remove () ؛ } // (2) إنشاء Threadlocal متغير ثابت static threadlocal <string> localVariable = new threadlocal <> () ؛ Public Static Void Main (string [] args) {// (3) إنشاء مؤشر ترابط واحد مؤشر ترابط واحد = مؤشر ترابط جديد (جديد RunNable () {public void run () {//3.1 قم بتعيين قيمة المتغير المحلي المحلي المحلي في مؤشر ترابط واحد محلي. System.out.println ("النتيجة بعد إزالة المتغير المحلي من الموضوع 1" + ":" + localvariable.get ()) ؛ // (4) قم بإنشاء مؤشر ترابط Two Thread threadtwo = new thread (new RunNable () {public void run () {//4.1 قم بتعيين قيمة المتغير المحلي المحلي المحلي في مؤشر ترابط واحد واحد محلي. 2 " +": " + localvariable.get ()) ؛}}) ؛ // (5) ابدأ الخيط threadone.start () ؛ threadtwo.start () ؛ }}الكود (2) ينشئ متغير مؤشر الترابط ؛
الرموز (3) و (4) إنشاء مؤشرات ترابط 1 و 2 على التوالي ؛
الرمز (5) يبدأ اثنين من المواضيع ؛
رمز 3.1 في الموضوع 1 يعين قيمة المحلية من خلال طريقة المجموعة. هذا الإعداد هو في الواقع نسخة في ذاكرة الموضوع 1. لا يمكن الوصول إلى هذه النسخة بواسطة مؤشر الترابط 2. ثم يستدعي رمز 3.2 وظيفة الطباعة ، والرمز 1.1 يحصل على قيمة المحلية المتغيرة في الذاكرة المحلية للمعلومات الحالية (مؤشر الترابط 1) من خلال وظيفة GET ؛
الموضوع 2 ينفذ مشابه لخيط 1.
نتائج التشغيل كما يلي:
هنا نحتاج إلى الانتباه لمشكلة تسرب الذاكرة في ThreadLocal
كل مؤشر ترابط لديه متغير عضو يسمى Threadlocals في الداخل. النوع المتغير هو hashmap. المفتاح هو هذا المرجع إلى متغير threadlocal الذي حددناه ، والقيمة هي القيمة عند تعييننا. يتم تخزين المتغير المحلي لكل مؤشر ترابط في متغير الذاكرة الخاص بالمعلومات. إذا لم يختفي الخيط الحالي ، فسيتم تخزين هذه المتغيرات المحلية حتى.
لذلك ، قد يتسبب ذلك في تسرب الذاكرة ، لذلك بعد استخدامه ، تذكر استدعاء طريقة إزالة ThreadLocal لحذف المتغيرات المحلية في الخيوط من الخيط المقابل.
بعد تفريغ التعليقات في الكود 1.2 ، قم بالتشغيل مرة أخرى ، ونتيجة التشغيل هي كما يلي:
هل فكرنا يومًا في سؤال كهذا: هل نحصل على قيمة المتغير المتغير المرصوف المحدد في مؤشر ترابط الأصل في خيط الطفل؟
هنا يمكننا أن نخبرك أنه لا يمكن الحصول على قيمة متغير Threadlocal في مؤشر ترابط الأصل في مؤشر ترابط الطفل. هل هناك طريقة للحصول على مؤشر ترابط الطفل للوصول إلى القيمة في موضوع الأصل؟ وراثيا بيرثريدولوكال ظهرت في المستقبل. وراثي ويرثراويدلوكال من الخيط ويوفر ميزة يمكن للأطفال الوصول إليها المتغيرات المحلية المحددة في الخيط الأصل.
أولاً ، دعنا نذهب إلى الكود المصدري لفئة erneritablethreadlocal التي يجب قراءتها ، على النحو التالي:
الطبقة العامة interitablethreadlocal <T> يمتد Threadlocal <T> {public erranitablethreadlocal () {} // (1) محمية t childvalue (t var1) {return var1 ؛ } // (2) threadlocalmap getMap (thread var1) {return var1.inheritablethreadlocals ؛ } // (3) void createMap (thread var1 ، t var2) {var1.inheritablethreadlocals = new threadlocalmap (this ، var2) ؛ }}يمكنك أن ترى أن وراثيًا وراثيًا ترفيهًا وترويشًا ثلاث طرق. تم وضع علامة على الرمز أعلاه. الكود (3) يمكن ملاحظة أن interitableThreadlocal يعيد كتابة طريقة CreateMap ، لذلك يمكن أن نرى أنه عندما يتم استدعاء طريقة المجموعة للمرة الأولى ، يتم إنشاء مثيل متغير interitablethreadlocals المتغير للمعلومات الحالية ، بدلاً من المجلدات.
الرمز (2) يمكنك أن تعرف أنه عندما يتم استدعاء طريقة GET للحصول على متغير الخريطة الداخلية للخيط الحالي ، يتم الحصول على ملاءمة العوامل ، وليس الخيوط.
النقطة الرئيسية موجودة هنا ، عند تنفيذ رمز إعادة الكتابة (1) ، وكيفية تنفيذ أن خيط الطفل يمكنه الوصول إلى المتغيرات المحلية لخيط الأصل. بدءًا من الكود الذي تم إنشاؤه بواسطة مؤشر ترابط ، فإن مُنشئ مؤشر الترابط ومُنشئ فئة Thread.java على النحو التالي:
/*** تم إنشاؤه بواسطة Cong في 2018/6/3. */ public thread (runnable target) {init (null ، target ، "thread-" + nextThreadNum () ، 0) ؛ } private void init (threadgroup g ، الهدف القابل للتشغيل ، اسم السلسلة ، المكدس الطويل ، AccessControlContext acc) {// ... // (4) احصل على مؤشر ترابط مؤشر ترابط الحالي = currentThread () ؛ // ... // (5) إذا كان متغير inheritableTheReadLocals من الخيط الأصل ليس فارغًا إذا (parent.inheritablethreadlocals! = null) // (6) ، فإن set errantablethreadlocals variable في this.inheritableTableThreadlocals = threadloCal.CreateinTiNateDmapApeTableTableTableTaLetAbleThReadAletaLeTaLeTaLetaLeTaLeTaLeTaLeTaLeTaLeTaLeTaLeTaLeTaLeTaLeTaLeTaLetAltaLeT. this.stackSize = stackSize ؛ tid = nextThreadId () ؛ }عند إنشاء مؤشر ترابط ، سيتم استدعاء طريقة init في المنشئ. كما ذكرنا سابقًا ، فإن GET inheritablethreadlocal GET وطريقة SET تعمل على تشغيل المتغير الموروثاتيرات ، لذلك سيتم تنفيذ المتغير الوراثي هنا ليس فارغًا ، لذلك سيتم تنفيذ الكود (6). دعونا نرى الكود المصدري لطريقة CreateInheritedMap ، على النحو التالي:
static threadlocalmap createInheritedMap (threadlocalmap parentMap) {return new threadlocalmap (parentMap) ؛ }يمكنك أن ترى أن CreateInheritedMap يستخدم متغير inheritableTheReadLocals من مؤشر ترابط الأصل كمنشئ لإنشاء متغير threadlocalmap جديد ، ثم يقوم بتعيينه إلى متغير interiTableTheReadLocals من مؤشر ترابط الطفل. ثم أدخل مُنشئ threadlocalmap. رمز المصدر كما يلي:
private threadlocalmap (threadlocalmap parentMap) {entry [] parentTable = parentMap.table ؛ int len = parentTable.length ؛ setThreshold (LEN) ؛ جدول = إدخال جديد [LEN] ؛ لـ (int j = 0 ؛ j <len ؛ j ++) {entry e = parentTable [j] ؛ if (e! = null) {suppressWarnings ("uncheced") threadlocal <object> key = (threadlocal <object>) e.get () ؛ if (key! = null) {// (7) استدعاء قيمة كائن طريقة تجاوز = key.childvalue ( int h = key.throadlocalhashCode & (len - 1) ؛ بينما (الجدول [h]! = null) h = nextIndex (h ، len) ؛ الجدول [H] = C ؛ حجم ++ ؛ }}}}}}ما يفعله الرمز أعلاه هو نسخ قيمة متغير عضو مؤشر ترابط الوالد المتغير إلى كائن ThreadLocalmap الجديد ، ويتم إعادة كتابة الكود (1) بواسطة الكود (7) من الفئة الموروثة.
بشكل عام: يعيد الفئة الوراثي readlocal كتابة الكود (2) و (3) ويحفظ المتغيرات المحلية في متغير الحالات الموروثة من الخيط المحدد. عندما يقوم مؤشر الترابط بتعيين المتغير من خلال المجموعة أو الحصول على طريقة لمثيل interiTableThreadLocals ، فإنه سيقوم بإنشاء متغير interiTableThreadlocals من الخيط الحالي. عندما ينشئ موضوع الأصل خيط الطفل ،
سيقوم المُنشئ بنسخ المتغير المحلي في متغير inheritableThreadlocals في مؤشر الترابط الأصل ونسخه إلى متغير interiTableTheReadLocals من مؤشر ترابط الطفل.
بعد أن يتم فهم المبدأ جيدًا ، دعونا نأخذ مثالًا للتحقق من ما نعرفه أعلاه ، على النحو التالي:
Package com.hjc ؛/*** تم إنشاؤه بواسطة Cong في 2018/6/3. */الفئة العامة erranitablethReadLocaltest {// (1) إنشاء مؤشر ترابط متغير متغير ثابت ثابت static <string> threadlocal = new threadlocal <string> () ؛ public static void main (string [] args) {// (2) set thread regiable. // (3) ابدأ مؤشر ترابط مؤشر ترابط مؤشر ترابط الطفل = جديد (جديد Runnable () {public void run () {// (4) تقوم قيمة مؤشر ترابط الطفل بإخراج System.out.println ("sub -brint:" + threadlocal.get ()) ؛}}) ؛ thread.start () ؛ // (5) يخرج مؤشر الترابط الرئيسي نظام القيمة المتغير. }}نتائج التشغيل كما يلي:
وهذا يعني ، بعد تعيين متغير Threadlocal نفسه في مؤشر الترابط الأصل ، لا يمكن الحصول عليه في سلسلة خيط الطفل. وفقًا للمقدمة الواردة في القسم السابق ، يجب أن تكون هذه ظاهرة طبيعية ، لأن الخيط الحالي هو مؤشر ترابط طفل عندما يتصل مؤشر ترابط الطفل بالطريقة GET ، ويتم استدعاء طريقة SET لتعيين متغير مؤشر الترابط هو مؤشر الترابط الرئيسي. هما الخيوط المختلفة ، وبطبيعة الحال يعيد خيط الطفل الفارغ عند الوصول.
هل هناك طريقة للحصول على مؤشر ترابط الطفل للوصول إلى القيمة في موضوع الأصل؟ الجواب هو نعم ، لذا استخدم الموروثات الحلقية التي تم تحليلها بواسطة مبدأنا أعلاه.
قم بتعديل الرمز (1) من المثال أعلاه إلى:
// (1) إنشاء مؤشر ترابط متغير ثابت ثابت static <string> threadlocal = new erranitablethreadlocal <string> () ؛
نتائج التشغيل كما يلي:
يمكن ملاحظة أنه يمكن الآن الحصول على قيمة متغير مؤشر الترابط بشكل طبيعي من مؤشر ترابط الطفل. لذلك في أي ظروف يحتاج الأطفال إلى الحصول على متغيرات threadlocal من الخيط الأصل؟
هناك الكثير من المواقف ، مثل متغير ThreadLocal الذي يخزن معلومات تسجيل الدخول للمستخدم. من المحتمل جدًا أن تحتاج الرسائل الفرعية أيضًا إلى استخدام معلومات تسجيل الدخول إلى المستخدم. على سبيل المثال ، تحتاج بعض البرامج الوسيطة إلى استخدام معرف تتبع موحد لتسجيل رابط الاتصال بأكمله.
استخدام threadlocal في فصل الربيع نطاق نطاق النطاق
نحن نعلم أنه عند تكوين حبة في XML في الربيع ، يمكنك تحديد سمة النطاق لتكوين نطاق الفاصوليا لتكون المفرد ، النموذج الأولي ، الطلب ، الجلسة ، إلخ. إذا كنت تريد أن يكون لدى حاوية الربيع نطاق الويب ،
بالإضافة إلى سمات النطاق المقابل المطلوبة لتكوين مستوى الفول ، يجب أيضًا تكوينه في web.xml على النحو التالي:
<StaNeer> <Segyer-Class> org.springframework.web.context.request.requestContextListener </leader-class> </stanker>
هنا ننظر أساسًا إلى طريقتين لـ requestContextListener:
طلب باطل عام (servletRequestevent requestevent)
و
طلب باطل عام (ServleTrequestevent requestevent)
عندما يأتي طلب الويب ، سيتم تنفيذ طريقة الطلب:
requestInitialized public void (servletRequestevent requestevent) {..... حذف httpservletrequest request = (httpservletrequest) requestevent.getServletRequest () ؛ ServleTRequestAttributes سمات = servletRequestAtTributes (طلب) ؛ request.setAttribute (request_attributes_attribute ، سمات) ؛ localecontextholder.setlocale (request.getlocale ()) ؛ . } public static void setRequestAtTributes (requestAttributes entributes) {setRequestAtTributes (السمات ، false) ؛ } setRequestAttRibutes الثابتة العامة (سمات requestAttRibutes ، وراثي منطقي) {if (entributes == null) {resetRequestatTributes () ؛ } else {// الافتراضي للوراثة = خطأ إذا (وراثي) {errantableReQuestAtTributeSholder.set (سمات) ؛ requestAttributeSholder.Remove () ؛ } آخر {requestAttributeSholder.set (سمات) ؛ erneritablerequestattributeSholder.remove () ؛ }}}يمكنك رؤية رمز المصدر أعلاه. نظرًا لأن الوراثة الافتراضية خاطئة ، يتم وضع قيم السمات الخاصة بنا في requestAtTributeshoder ، وتعريفه هو:
private Static Final ThreadLocal <SquertaTTributes> requestAtTributeSholder = new thereThReadLocal <SquergeAtTributes> ("سمات الطلب") ؛ private Static Final Threadlocal <SquertAttributes> internitablerequestattributeSholder = newInherItableTableTheReadLocal <SquertAttributes> ("سياق الطلب") ؛من بينها ، يمتد NamptThreadlocal <T> Threadlocal <T> ، لذلك فهو غير قابل للوراثة.
من بينها ، يمتد NamptThreadlocal <T> Threadlocal <T> ، لذلك فهو غير قابل للوراثة.
يمتد NameInherItAblethReadLocal <T> الموروثات المرتبطة <T> ، لذلك لا يمكن الحصول على قيمة السمة الموضوعة في requestContexholder بشكل افتراضي في مؤشر ترابط الطفل.
عندما ينتهي الطلب ، يتم استدعاء طريقة requestDestroyed ، والرمز المصدر هو كما يلي:
requestdestroyed public requestDestroyed (servletrequestevent requestevent) {servletRequestAtTributes = (servletRequestAttributes) requestevent.getServletRequest (). getAttribute (request_attributes_attribute) ؛ servletRequestAttributes threadAttributes = (servletRequestatTributes) requestContextholder.getRequestAttributes () ؛ if (ThreadAttributes! = null) {// من المحتمل جدًا مسح مؤشر ترابط مؤشر الترابط الحالي في مؤشر ترابط الطلب الأولي if (attributes == null) {attributes = threadattributes ؛ } // عندما ينتهي الطلب ، قم بمسح متغير مؤشر الترابط في مؤشر الترابط الحالي. localecontextholder.ResetLocaleContext () ؛ requestContextholder.ResetRequestAttributes () ؛ } if (entributes! = null) {attributes.requestCompleted () ؛ }}بعد ذلك ، دعونا نلقي نظرة على منطق الاتصال بطلب الويب من مخطط التوقيت:
بمعنى أنه في كل مرة يتم فيها بدء طلب الويب قبل معالجة السياق (تطبيق محدد) في Tomcat ، سيتم تعيين خاصية requestContexTholder بعد مطابقة المضيف ، بحيث لا يكون requestAttributeSholder فارغًا وسيتم مسحه في نهاية الطلب.
لذلك ، بشكل افتراضي ، لا يمكن الوصول إلى مؤشرات ترابط السمة الموضوعة في جهاز requestContextholder ، ويتم تنفيذ الفاصوليا في نطاق طلب الربيع باستخدام Threadlocal.
بعد ذلك ، يتم تنفيذ طلب محاكاة مثال ، الرمز كما يلي:
تكوين web.xml كما يلي:
نظرًا لأنه نطاق طلب ، يجب أن يكون مشروع ويب ويجب تكوين requestContextListener إلى web.xml.
<StaNeer> <Segyer-Class> org.springframework.web.context.request.requestContextListener </leader-class> </stanker>
ثم حقن حبة نطاق الطلب في حاوية IOC. الرمز كما يلي:
<bean id = "requestbean" scope = "request"> <property name = "name" value = "hjc" /> <aop: scoped-proxy /> </bean>
رمز الاختبار كما يلي:
webResource ("/testService") الفئة العامة testrpc {autowired requestBean requestInfo ؛ ResourCemapping ("test") اختبار الإجراء العام (سياق errorContext) {ActionResult result = new ActionResult () ؛ pvginfo.setName ("HJC") ؛ اسم السلسلة = requestInfo.getName () ؛ result.setValue (الاسم) ؛ نتيجة العودة }}كما ذكر أعلاه ، قم أولاً بتكوين requestContextListener في web.xml ، ثم حقن مثيل requestBean في حاوية IOC مع نطاق الطلب. أخيرًا ، يتم حقن مثيل requestBean في TestRPC. Method Test First يقوم بالاتصال بالطريقة SETNAME SETNAME لتعيين سمة الاسم ، ثم احصل على سمة الاسم والعودة.
هنا ، إذا كان كائن requestInfo هو Singleton ، بعد أن اتصل بمصادر متعددة طريقة الاختبار في نفس الوقت ، يكون كل مؤشر ترابط عملية مجموعة. هذه العملية ليست ذرية وستتسبب في مشاكل سلامة الخيط. النطاق المعلن هنا هو مستوى الطلب ، ولكل مؤشر ترابط متغير محلي مع requestInfo.
مخطط التوقيت لطلب طريقة المثال أعلاه هو كما يلي:
نحتاج إلى التركيز على ما يحدث عند استدعاء اختبار:
في الواقع ، فإن الطلب الذي تم إنشاؤه في وقت سابق هو بعد أن تم تحريكه بواسطة CGLIB (إذا كنت مهتمًا ، فيمكنك دراسة SCOPEDPROXYFACTORYBEAN وأنواع أخرى) ، لذلك عند الاتصال بـ setName أو getName ، سيتم اعتراضك بواسطة InydamicAdvisedInterpector. سيقوم المعترض في النهاية بالاتصال بأسلوب GET of requestScope للحصول على المتغيرات المحلية التي يحتفظ بها الخيط الحالي.
المفتاح هنا. نحتاج إلى إلقاء نظرة على الكود المصدري لـ requestScope GET على النحو التالي:
الكائن العام الحصول على (اسم السلسلة ، ObjectFactory ObjectFactory) {requestAtTributes السمات = requestContexTholder.CurrentRequestAtTributes () ؛ // (1) Object scopedObject = attriButes.getAttribute (name ، getScope ()) ؛ if (scopedobject == null) {scopedObject = objectFactory.getObject () ؛ // (2) entributes.setAttribute (name ، scopedobject ، getScope ()) ؛ // (3)} return scopedobject ؛ }يمكن ملاحظة أنه عند بدء طلب ما ، سيتم تعيين requestAttributeSholder عن طريق استدعاء requestContextListener.requestinitialized في requestContextListener.setRequestAttributess.
ثم بعد توجيه الطلب إلى طريقة اختبار TestRPC ، في المرة الأولى التي يتم فيها استدعاء طريقة setName في طريقة الاختبار ، سيتم استدعاء طريقة requestScope.get (). يحصل الرمز في طريقة GET (1) على قيمة مجموعة السمات المحفوظة بواسطة متغير مؤشر الترابط المحلي المحدد الذي تم تعيينه من خلال requestContextListener.requestinitialized.
ثم تحقق مما إذا كانت هناك سمة تسمى requestInfo في مجموعة السمات. نظرًا لأنها المكالمة الأولى ، فهي غير موجودة ، لذلك سيتم تنفيذ الكود (2) والسماح لـ Spring بإنشاء كائن requestInfo ، ثم قم بتعيينه على سمات مجموعة السمات ، أي أنه سيتم حفظه في الذاكرة المحلية لخيط الطلب الحالي. ثم أعد الكائن الذي تم إنشاؤه واتصل اسم الكائن الذي تم إنشاؤه.
أخيرًا ، يتم استدعاء طريقة getName في طريقة الاختبار ، وسيتم استدعاء طريقة requestscope.get (). يحصل الكود في طريقة GET (1) على ترابط المتغير المحلي الذي تم تعيينه من خلال requestContextListener.requestinitialized ، ثم معرفة ما إذا كانت هناك سمة تسمى requestInfo في مجموعة السمات.
نظرًا لأن Bean TherpessInfo قد تم تعيينه على متغير ThreadLocal عندما يتم استدعاؤه لـ SetName لأول مرة ، ويتم استدعاء نفس الخيط لـ SetName و GetName ، فإن كائن requestInfo الذي تم إنشاؤه عند استدعاء اسم setName يتم إرجاعه مباشرة هنا ، ثم يتم استدعاء طريقة getName الخاصة به.
حتى الآن ، فهمنا مبدأ التنفيذ لـ Threadlocal وأشارنا إلى أن Threadlocal لا يدعم الميراث ؛ ثم شرحنا على الفور كيف تعوض وراثي readileThreadlocal عن الميزة التي لا تدعم threadlocal الميراث ؛ أخيرًا ، قدمنا بإيجاز كيفية استخدام Threadlocal في إطار الربيع لتنفيذ نطاق نطاق reqeust.
لخص
ما سبق هو المحتوى الكامل لهذه المقالة. آمل أن يكون لمحتوى هذه المقالة قيمة مرجعية معينة لدراسة أو عمل الجميع. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل. شكرا لك على دعمك إلى wulin.com.