1. تحليل رمز مصدر الفئة غير الآمن
توفر الفئة غير الآمنة في حزمة RT.JAR من JDK عمليات ذرية على مستوى الأجهزة. الأساليب في غير آمنة كلها طرق أصلية ، ويتم الوصول إلى مكتبات تنفيذ C ++ المحلية باستخدام JNI.
شرح الوظائف الرئيسية للفئة غير الآمنة في RT.JAR. توفر الفئة غير الآمنة عمليات ذرية على مستوى الأجهزة ويمكنها تشغيل متغيرات الذاكرة بأمان مباشرة. يستخدم على نطاق واسع في رمز مصدر JUC. يضع فهم مبادئها الأساس لدراسة رمز مصدر JUC.
أولاً ، دعونا نفهم استخدام الأساليب الرئيسية في الفصل غير الآمن ، على النحو التالي:
1. LONG ObjectFieldOffset (حقل الحقل) الطريقة: إرجاع عنوان إزاحة الذاكرة للمتغير المحدد في الفئة التي ينتمي إليها. يتم استخدام عنوان الإزاحة فقط عند الوصول إلى الحقل المحدد في الدالة غير الآمنة. يستخدم الرمز التالي غير آمن للحصول على إزاحة الذاكرة للقيمة المتغيرة في Atomiclong في كائن Atomiclong. الرمز كما يلي:
ثابت {try {valueffset = unsafe.objectfieldoffset (atomiclong.class.getDeclaredfield ("value")) ؛ } catch (استثناء ex) {رمي خطأ جديد (ex) ؛ }}2. في ArrayBaseOffset (Class ArrayClass) طريقة: احصل على عنوان العنصر الأول في المصفوفة
3-TrayIndexScale (ArrayClass) طريقة: احصل على عدد البايتات التي يشغلها عنصر واحد في المصفوفة
3.Boolean CompareAndswaplong (Object OBJ ، إزاحة طويلة ، متوقع طويل ، تحديث طويل): قارن ما إذا كانت قيمة متغير إزاحة الإزاحة في الكائن OBJ تساوي توقعها. إذا كان ذلك متساويًا ، فسيتم تحديثه بقيمة التحديث ، ثم يتم إرجاعه بشكل صحيح ، وإلا فإنه سيعود خطأ.
4. Public Native Long GetLongvolative (Object OBJ ، Orvester): احصل على قيمة دلالات الذاكرة المتطايرة المقابلة لمتغير إزاحة الإزاحة في الكائن OBJ.
5.void putorderDedLong (Object OBJ ، إزاحة طويلة ، القيمة الطويلة) الطريقة: اضبط قيمة الحقل الطويل المقابل لعنوان إزاحة الإزاحة في كائن OBJ إلى القيمة. هذه هي طريقة putlongvolatile مع تأخير ، ولا تضمن أن تعديل القيمة سيكون مرئيًا على الفور لخيوط أخرى. تكون المتغيرات مفيدة فقط إذا تم تعديلها مع متقلبة ويتوقع تعديلها بشكل غير متوقع.
6.Doid Park (Isabsolute المنطقي ، وقت طويل) الطريقة: منع الخيط الحالي. عندما تكون المعلمة Isabsolute مساوية للخطأ ، فإن الوقت يساوي 0 يعني الحجب طوال الوقت. الوقت أكبر من 0 يعني أن خيط الحظر سيتم إيقاظه بعد انتظار الوقت المحدد. هذه المرة هي قيمة نسبية ، وهي قيمة تدريجية ، أي أن الخيط الحالي سيتم إيقاظه بعد تراكم الوقت بالنسبة للوقت الحالي. إذا كان Isabsolute مساوياً للصواب والوقت أكبر من 0 ، فهذا يعني أنه سيتم إيقاظه بعد الحظر إلى النقطة الزمنية المحددة. الوقت هنا هو الوقت المطلق ، وهو القيمة التي تم تحويلها إلى مرض التصلب العصبي المتعدد في نقطة زمنية معينة. بالإضافة إلى ذلك ، عندما تقوم مؤشرات الترابط الأخرى بتسوية طريقة المقاطعة لخيط الحظر الحالي ومقاطعة مؤشر الترابط الحالي ، سيعود مؤشر الترابط الحالي أيضًا. عندما تتصل مؤشرات الترابط الأخرى بالطريقة unpark واتخذ مؤشر الترابط الحالي كمعلمة ، سيعود مؤشر الترابط الحالي أيضًا.
7.void unpark (مؤشر ترابط الكائن) الطريقة: استيقظ مؤشر ترابط الحظر بعد استدعاء الحديقة ، والمعلمات هي مؤشرات الترابط التي تحتاج إلى إيقاظها.
تمت إضافة عدة طرق جديدة إلى JDK1.8. فيما يلي قائمة بسيطة بطرق تشغيل النوع الطويل على النحو التالي:
8. لمدة لا تكتسب getandsetlong (Object OBJ ، الإزاحة الطويلة ، التحديث الطويل): احصل على قيمة الدلالات المتطايرة المتغيرة مع الإزاحة في الكائن OBJ ، وتعيين قيمة الدلالات المتطايرة المتغيرة للتحديث. طريقة الاستخدام هي كما يلي:
Public Final Long GetAndSetLong (Object OBJ ، Ongrofse Long ، Update) {long l ؛ do {l = getlongvolatile (obj ، ارhfset) ؛ // (1)} بينما (! compareandswaplong (OBJ ، Offset ، l ، update)) ؛ العودة ل ؛ }من الكود الداخلي (1) ، يمكنك استخدام GetLongVolative للحصول على قيمة المتغير الحالي ، ثم استخدام عملية CAS Atomic لتعيين القيمة الجديدة. هنا ، باستخدام الحلقات يأخذ في الاعتبار الموقف الذي تتصل فيه مؤشرات ترابط متعددة في نفس الوقت ، ثم مطلوب استرجاع الدوران بعد فشل CAS.
9.LONG GETANDADDLONG (OBORIN OBJ ، إزاحة طويلة ، ADDVALUE الطويل): احصل على قيمة دلالات المتغير المتغير مع الإزاحة في الكائن OBJ ، وتعيين القيمة المتغيرة على القيمة الأصلية + ADDVALUE. طريقة الاستخدام هي كما يلي:
Public Final Long GetAndaddlong (Object OBJ ، Ongrofse Long ، AddValue Long) {long l ؛ do {l = getlongvolatile (obj ، ارhfset) ؛ } بينما (! compareAndSwapLong (OBJ ، Offset ، l ، l + addValue)) ؛ العودة ل ؛ }على غرار تنفيذ getAndSetLong ، باستثناء أنه عند استخدام CAS هنا ، يتم استخدام القيمة الأصلية + قيمة المعلمة الإضافية المنقولة.
إذن كيف تستخدم الفئة غير الآمنة؟
رؤية أن غير آمن رائع للغاية ، هل تريد حقًا التدرب؟ حسنًا ، دعونا نلقي نظرة أولاً على الكود التالي:
Package com.hjc ؛ import sun.misc.unsafe ؛/*** تم إنشاؤه بواسطة Cong في 2018/6/6. */فئة عامة TestUnsafe {// احصل على مثيل غير آمن (2.2.1) غير آمن غير آمن = unfafe.getunsafe () ؛ // سجل قيمة الإزاحة للحالة المتغيرة في الفئة Testunsafe (2.2.2) Static Final StateOffset ؛ // متغير (2.2.3) الحالة الطويلة المتقلبة الخاصة = 0 ؛ ثابت {try {// الحصول على قيمة الإزاحة لمتغير الحالة في الفئة testunsafe (2.2.4) stateOffset = unfafe.objectfieldoffset (testunsafe.class.getDeclaredfield ("state")) ؛ } catch (استثناء ex) {system.out.println (ex.getLocalizedMessage ()) ؛ رمي خطأ جديد (ex) ؛ }} public static void main (string [] args) {// إنشاء مثيل وضبط قيمة الحالة على 1 (2.2.5) testunsafe test = new testunsafe () ؛ //(2.2.6) boolean suctess = unfafe. system.out.println (suct) ؛ }}الكود (2.2.1) يحصل على مثيل غير آمن ، والرمز (2.2.3) ينشئ حالة متغيرة تهيئتها إلى 0.
يستخدم الكود (2.2.4) Unsafe.ObjectFieldOffset للحصول على عنوان إزاحة الذاكرة لمتغير الحالة في فئة TestUnsafe في كائن Testunsafe وحفظه إلى متغير StateOffset.
الكود (2.2.6) يستدعي طريقة المقارنة في مثيل غير آمن تم إنشاؤه ويحدد قيمة متغير الحالة لكائن الاختبار. على وجه التحديد ، إذا كان متغير الحالة الذي تعرضه لذاكرة كائن الاختبار هو stateOffset هو 0 ، يتم تغيير قيمة التحديث إلى 1.
نريد إدخال True في الكود أعلاه ، ولكن بعد التنفيذ ، سيتم إخراج النتائج التالية:
لماذا هذا يحدث؟ يجب عليك إدخال رمز getunsafe ، مثل معرفة ما يتم فيه:
استاتيكي خاص نهائي غير آمن غير آمن = جديد غير آمن () ؛ static public unsafe getunsafe () {// (2.2.7) class localClass = Reflection.getCallerClass () ؛ // (2.2.8) if (! vm.issystemdomainloader (localclass.getClassLoader ())) {رمي New SecurityException ("غير آمن") ؛ } إرجاع theUnsafe ؛} // الحكم على ما إذا كان paramclassloader هو محمل فئة bootstrap (2.2.9) issystemdomainloader isstemdomainloader (classloader paramclassloader) {return paramclasslader == null ؛ }الكود (2.2.7) يحصل على كائن الفئة للكائن الذي يستدعي getUnsafe ، وهنا testunsafe.cals.
يحدد الكود (2.2.8) ما إذا كان هو المحلي الذي تم تحميله بواسطة محمل فئة bootstrap. المفتاح هنا هو ما إذا كان محمل bootstrap يحمل testunsafe.class. أولئك الذين شاهدوا آلية تحميل فئة الجهاز الافتراضي Java يرون بوضوح أن ذلك يرجع إلى أن testunsafe.class يتم تحميلها باستخدام AppClassloader ، لذلك يتم طرح استثناء مباشرة هنا.
لذا فإن السؤال هو ، لماذا تحتاج إلى إصدار هذا الحكم؟
نحن نعلم أنه يتم توفير الفئة غير الآمنة في RT.JAR ، ويتم تحميل الفصل في RT.JAR باستخدام محمل فئة Bootstrap. يتم تحميل الفئة التي نبدأ فيها الوظيفة الرئيسية باستخدام AppClassloader ، لذلك عند تحميل الفئة غير الآمنة في الوظيفة الرئيسية ، بالنظر إلى أن آلية التفويض الأصل ستعمل على تمويل إلى Bootstrap لتحميل الفئة غير الآمنة.
إذا لم يكن هناك مصادقة للرمز (2.2.8) ، فيمكن لتطبيقنا استخدام غير آمن للقيام بالأشياء حسب الرغبة. يمكن للفئة غير الآمنة تشغيل الذاكرة مباشرة ، وهو أمر غير آمن للغاية. لذلك ، قام فريق تطوير JDK بشكل خاص بهذا التقييد ، ولم يسمح للمطورين باستخدام فئة غير آمنة بموجب القنوات العادية ، ولكن استخدام وظائف غير آمنة في الفئة الأساسية في RT.JAR.
والسؤال هو ، ماذا يجب أن نفعل إذا كنا نريد حقًا إنشاء فئة غير آمنة واستخدام الوظيفة غير الآمنة؟
يجب ألا ننسى التكنولوجيا السوداء للتفكير ، وأن نستخدم انعكاسًا عالميًا للحصول على طريقة مثيل Unfafe. الرمز كما يلي:
package com.hjc ؛ import sun.misc.unsafe ؛ import java.lang.reflect.field ؛/*** تم إنشاؤه بواسطة Cong في 2018/6/6. */فئة عامة Testunsafe {Static Final Unfafe ؛ مستقلة طويلة نهائية stateoffset ؛ الحالة الطويلة المتقلبة الخاصة = 0 ؛ ثابت {try {// reflect fo the unsafe fream field theunsafe (2.2.10) field field = unsafe.class.getDeclaredfield ("theunsafe") ؛ // ضبط على الوصول (2.2.11) Field.SetAccessible (true) ؛ // احصل على قيمة هذا المتغير (2.2.12) غير آمن = (غير آمن). // احصل على إزاحة الحالة في Testunsafe (2.2.13) stateOffset = unfafe.objectfieldoffset (testunsafe.class.getDeclaredfield ("State")) ؛ } catch (استثناء ex) {system.out.println (ex.getLocalizedMessage ()) ؛ رمي خطأ جديد (ex) ؛ }} public static void main (string [] args) {testunsafe test = new testunsafe () ؛ Boolean Success = Untafe.compareanswapint (Test ، StateOffset ، 0 ، 1) ؛ system.out.println (suct) ؛ }}إذا كان الكود أعلاه (2.2.10 2.2.11 2.2.12) يعكس مثال غير آمن ، فإن النتيجة الجارية هي كما يلي:
2. البحث على رمز المصدر لفئة Locksupport
Locksupport في RT.JAR في JDK هي فئة الأدوات ، ووظائفها الرئيسية هي تعليق وتستيقظ الخيوط. هذا هو الأساس لإنشاء أقفال وفئات التزامن الأخرى.
سوف ترتبط فئة LockSupport مع كل مؤشر ترابط يستخدمه. لا يحمل الخيط الذي يدعو إلى طريقة فئة LockSupport افتراضيًا ترخيصًا. يتم تطبيق Locksupport داخليًا باستخدام الفصل غير الآمن.
هنا يجب أن نولي اهتمامًا للعديد من الوظائف المهمة لـ Locksupport ، على النحو التالي:
1. فود بارك () الطريقة: إذا كان مؤشر الترابط استدعاء بارك () قد حصل على الترخيص المرتبط بـ LockSupport ، فسيعود استدعاء Locksupport.park () على الفور. خلاف ذلك ، سيتم حظر سلسلة الاتصال من المشاركة في جدولة موضوع ، أي أنه سيتم حظره وتعليقه. الرمز التالي هو المثال التالي:
package com.hjc ؛ import java.util.concurrent.locks.locksupport ؛/*** تم إنشاؤه بواسطة Cong في 2018/6/6. */الفئة العامة locksupporttest {public static void main (string [] args) {system.out.println ("Park Start!") ؛ locksupport.park () ؛ System.out.println ("Park Stop!") ؛ }}كما هو موضح في الكود أعلاه ، اتصل مباشرة بطريقة Park في الوظيفة الرئيسية ، وستقوم النتيجة النهائية فقط بإخراج الحديقة! ثم سيتم تعليق الخيط الحالي ، لأن مؤشر ترابط الاتصال لا يحتفظ بترخيص افتراضيًا. نتائج التشغيل كما يلي:
عندما ترى مؤشرات الترابط الأخرى ، اتصل بالطريقة Unpark (مؤشر ترابط مؤشر ترابط) ويتم استخدام مؤشر الترابط الحالي كمعلمة ، وسيعود مؤشر الترابط الذي يستدعي طريقة الحديقة. بالإضافة إلى ذلك ، تقوم مؤشرات الترابط الأخرى باستدعاء طريقة Interrupt () لخيط الحظر. عندما يتم تعيين علامة المقاطعة أو سيعود مؤشر ترابط الحظر بعد الاستيقاظ الخاطئ للموضوع ، من الأفضل استخدام شروط الحلقة للحكم.
تجدر الإشارة إلى أن الخيط الذي يستدعي طريقة Park () المحظورة يتم مقاطعة مؤشرات الترابط الأخرى وإرجاع الخيط المحظور ، لن يرمي استثناء من المقاطع.
2. طريقة unpark (مؤشر ترابط مؤشر ترابط) عندما يقوم مؤشر ترابط بتوصيل Unpark ، إذا كان مؤشر ترابط المعلمة لا يحتفظ بالترخيص المرتبط بالمعلومات وفئة LockSupport ، فدع مؤشر الترابط يحتفظ به. إذا تم تعليق الخيط المسمى Park () من قبل وتم استدعاء الخيط ، فسيتم إيقاظ الموضوع بعد استدعاء Unpark.
إذا لم يتصل الخيط بارك من قبل ، بعد استدعاء طريقة unpark ، سيتم إرجاع طريقة Park () على الفور. تم تعديل الرمز أعلاه على النحو التالي:
package com.hjc ؛ import java.util.concurrent.locks.locksupport ؛/*** تم إنشاؤه بواسطة Cong في 2018/6/6. */الفئة العامة locksupporttest {public static void main (string [] args) {system.out.println ("Park Start!") ؛ // جعل الموضوع الحالي الحصول على LockSupport.unpark (thread.currentThRead ()) ؛ // Call Park Locksupport.park () مرة أخرى ؛ System.out.println ("Park Stop!") ؛ }}نتائج التشغيل كما يلي:
بعد ذلك ، نحن نبحث في مثال لتعميق فهمنا للحديقة ، unpark ، الرمز هو كما يلي:
استيراد java.util.concurrent.locks.locksupport ؛/*** تم إنشاؤه بواسطة Cong في 2018/6/6. */الفئة العامة locksupporttest {public static void main (string [] args) remrows interruptedException {thread thread = new thread (new runnable () { @ouver public run () {system.out.println ("child thread park start!") ؛ }}) ؛ // ابدأ سلسلة موضوع الطفل. ستارت () ؛ // ينام الخيط الرئيسي 1S.sleep (1000) ؛ System.out.println ("The Main Thread Unpark Start!") ؛ // استدعاء Unpark للسماح للموضوع باحتفاظ الترخيص ، ثم ستعيد طريقة Park Locksupport.unpark (موضوع) ؛ }}نتائج التشغيل كما يلي:
الرمز أعلاه أولاً ينشئ مؤشر ترابط طفل. بعد بدء التشغيل ، يدعو خيط الطفل طريقة الحديقة. نظرًا لأن موضوع الطفل الافتراضي لا يحمل ترخيصًا ، فإنه سيعلق نفسه.
الخيط الرئيسي ينام لمدة 1 ثانية. والغرض من ذلك هو أن الخيط الرئيسي يستدعي طريقة UNPARK ويتيح لخيط الطفل إخراج حديقة خيط الطفل! وكوك.
ثم يقوم الخيط الرئيسي بتنفيذ طريقة UNPARK ، مع كون المعلمة هي خيط الطفل ، والغرض من ذلك هو السماح لخيط الطفل باحتفاظ الترخيص ، ثم طريقة الحديقة التي يطلق عليها مؤشر ترابط الطفل.
عند إعادة طريقة الحديقة ، لن يخبرك ما هو السبب الذي يعود إليه. لذلك ، يحتاج المتصل إلى التحقق مرة أخرى مما إذا كانت الحالة راضية بناءً على طريقة Park التي كان في المكالمة الحالية. إذا لم يتم الوفاء به ، فهو بحاجة إلى استدعاء طريقة الحديقة مرة أخرى.
على سبيل المثال ، حالة المقاطعة لخيط واحد عند إرجاعها ، يمكن تحديد ما إذا كان يتم إرجاعه لأنه متقطع بناءً على مقارنة حالة المقاطعة قبل وبعد المكالمة.
لتوضيح أن الخيط بعد استدعاء طريقة الحديقة سيعود بعد مقاطعته ، قم بتعديل رمز المثال أعلاه وحذف locksupport.unpark (موضوع) ؛ ثم إضافة thread.interrupt () ؛ الرمز كما يلي:
استيراد java.util.concurrent.locks.locksupport ؛/*** تم إنشاؤه بواسطة Cong في 2018/6/6. */فئة عامة قفال kilsupporttest {public static void main (string [] args) remrows interruptedException {thread thread = new thread (new runnable () {Override public void run () (! // ابدأ خيط الطفل. START () ؛ // Thread Main Sleeps 1S Thread.sleep (1000) ؛ System.out.println ("The Main Thread Unpark Start!") ؛ // Interrupt Child Thread.Interrupt () ؛ }}نتائج التشغيل كما يلي:
مثل الرمز أعلاه ، سينتهي مؤشر ترابط الطفل إلا بعد مقاطعة خيط الطفل. إذا لم يتم مقاطعة خيط الطفل ، حتى إذا استدعت Unpark (موضوع) ، فلن ينتهي مؤشر ترابط الطفل.
3. طريقة parknanos (nanos الطويلة): على غرار الحديقة ، إذا كان مؤشر الترابط استدعاء الحديقة قد حصل على الترخيص المرتبط بـ Locksupport ، فإن استدعاء locksupport.park () سيعود على الفور. الفرق هو أنه إذا لم يتم الحصول على مؤشر الترابط الذي يتصل بخيط الخيط ، فسيتم تعليقه ثم يعود بعد وقت النانو.
يدعم Park أيضًا ثلاث طرق مع معلمات الحاصرات. عندما يستدعي مؤشر ترابط الحديقة دون عقد ترخيص وتم حظره وتعليقه ، سيتم تسجيل كائن المانع داخل الخيط.
استخدم أدوات التشخيص لمراقبة سبب حظر الخيط. تستخدم أدوات التشخيص طريقة getBlocker (Thread) للحصول على كائن Blocker. لذلك ، توصي JDK باستخدام طريقة Park مع معلمات الحاصرات وضبط الحاصرات على ذلك ، لذلك عندما تقوم Memory Dump بإصلاح المشكلة ، يمكننا معرفة الفئة التي يتم حظرها.
الأمثلة على النحو التالي:
استيراد java.util.concurrent.locks.locksupport ؛/*** تم إنشاؤه بواسطة Cong في 2018/6/6. */الفئة العامة testpark {public void testpark () {locksupport.park () ؛ // (1)} main static void static (string [] args) {testpark testpark = new testpark () ؛ testpark.testpark () ؛ }}نتائج التشغيل كما يلي:
يمكنك أن ترى أنها تعمل على منع الحظر ، لذلك نحتاج إلى استخدام الأدوات الموجودة في دليل JDK/BIN لإلقاء نظرة. إذا كنت لا تعرف ، فمن المستحسن إلقاء نظرة على أدوات مراقبة JVM أولاً.
عند استخدام JSTACK PID لعرض مكدس مؤشر الترابط بعد التشغيل ، فإن النتائج التي يمكنك رؤيتها هي كما يلي:
ثم نقوم بتعديل الكود أعلاه (1) على النحو التالي:
locksupport.park (هذا) ؛ // (1)
قم بتشغيله مرة أخرى واستخدم JSTACK PID لعرض النتائج على النحو التالي:
يمكن ملاحظة أنه بعد طريقة الحديقة مع Blocker ، يمكن أن توفر مكدس مؤشر الترابط المزيد من المعلومات حول حظر الكائنات.
بعد ذلك ، سوف نتحقق من الدالة المصدري لدالة Park (Compion Blocker) ، والرمز المصدر هو كما يلي:
Public Static Void Park (كائن مانع) {// احصل على مؤشر ترابط الاتصال t = thread.currentThRead () ؛ // قم بتعيين SetBlocker المتغير المانع (T ، Blocker) ؛ // شنق الموضوع غير آمن. park (false ، 0l) ؛ // امسح متغير الحظر بعد تنشيط مؤشر الترابط ، لأنه يتم تحليل السبب عادةً عند حظر الخيط setBlocker (t ، null) ؛}هناك متغير في فئة مؤشرات الترابط الكائن المتطاير. يتم استخدامه لتخزين كائن الحاصرات الذي تم تمريره بواسطة الحديقة ، أي ، يتم تخزين متغير الحظر في متغير العضو من مؤشر الترابط الذي يدعو إلى طريقة الحديقة.
4. وظيفة Parknanos الفراغ (مانع الكائن ، النانو الطويل) لها وقت مهلة إضافي مقارنة مع Park (كائن مانع).
5-
Public Static Void parkuntil (مانع الكائن ، الموعد النهائي الطويل) {thread t = thread.currentThRead () ؛ setBlocker (T ، Blocker) ؛ // isabsolute = true ، الوقت = الموعد النهائي ؛ يعني أن غير آمن. setBlocker (t ، null) ؛ }يمكنك أن ترى أنه موعد نهائي محدد ، فإن الوحدة الزمنية هي المللي ثانية ، والتي يتم تحويلها إلى قيمة بعد ميلي ثانية من عام 1970 إلى النقطة الزمنية الحالية. الفرق بين هذا و parknanos (مانع الكائن ، النانو الطويل) هو أن الأخير يحسب الوقت النانو للانتظار من الوقت الحالي ، بينما يحدد الأول نقطة زمنية.
على سبيل المثال ، نحتاج إلى الانتظار حتى 20:34 في 2018.06.06 ، ثم تحويل هذه النقطة الزمنية إلى إجمالي عدد المللي ثانية من عام 1970 إلى هذه النقطة الزمنية.
دعونا نلقي نظرة على مثال آخر ، الرمز كما يلي:
استيراد java.util.queue ؛ استيراد java.util.concurrent.concurrentLinkedqueue ؛ import java.util.concurrent.atomic.atomicboolean ؛ import java.util.concurrent.locksupport ؛ */public class fifomutex {private final final atomicboolean locked = new AtomicBoolean (false) ؛ قائمة الانتظار النهائية الخاصة <Thown> Waiders = New ConcurrentLinkedQueue <froof> () ؛ قفل void العام () {boolean تم انقطاع = false ؛ Thread Current = thread.currentThRead () ؛ Waiders.Add (الحالي) ؛ // فقط يمكن لخيط رأس الفريق الحصول على القفل (1) بينما (waiders.peek ()! = current ||! locked.compareanset (خطأ ، حقيقي)) {locksupport.park (this) ؛ if (thread.interrupted ()) // (2) تم انقطاع = true ؛ } waiters.remove () ؛ if (تم انقطاع) // (3) current.Interrupt () ؛ } public void inlock () {locked.set (false) ؛ locksupport.unpark (waiders.peek ()) ؛ }}يمكنك أن ترى أن هذا هو قفل الأول في الأول ، أي فقط عنصر رأس قائمة الانتظار يمكنه الحصول عليه. الكود (1) إذا لم يكن مؤشر الترابط الحالي هو رأس قائمة الانتظار أو تم الحصول على القفل الحالي بواسطة مؤشرات ترابط أخرى ، فاستدعاء طريقة الحديقة لتعليق نفسها.
ثم رمز (2) يصدر حكمًا. إذا تم إرجاع طريقة الحديقة نظرًا لأنه متقطع ، يتم تجاهل المقاطعة ، ويتم إعادة تعيين علامة المقاطعة ، ويتم إجراء علامة فقط ، ثم تحديد ما إذا كان الخيط الحالي هو العنصر الرئيسي في قائمة الانتظار أو ما إذا كان قد تم الحصول على القفل بواسطة مؤشرات ترابط أخرى. إذا كان الأمر كذلك ، فاستمر في استدعاء طريقة الحديقة لتعليق نفسك.
ثم إذا كانت العلامة صحيحة في الكود (3) ، فسيتم مقاطعة مؤشر الترابط. كيف تفهم هذا؟ في الواقع ، قاطعت المواضيع الأخرى الخيط. على الرغم من أنني لست مهتمًا بإشارة المقاطعة وتجاهلها ، إلا أنه لا يعني أن المواضيع الأخرى ليست مهتمة بالعلم ، لذلك أحتاج إلى استعادتها.
لخص
ما سبق هو المحتوى الكامل لهذه المقالة. آمل أن يكون لمحتوى هذه المقالة قيمة مرجعية معينة لدراسة أو عمل الجميع. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل. شكرا لك على دعمك إلى wulin.com.