مخطط حالة الموضوع
وتشمل المواضيع الحالات الخمس التالية.
1. حالة جديدة: بعد إنشاء كائن الخيط ، يدخل الحالة الجديدة. على سبيل المثال ، مؤشر الترابط = موضوع جديد ().
2. حالة جاهزة (Runnable): المعروف أيضًا باسم "الحالة القابلة للتنفيذ". بعد إنشاء كائن مؤشر الترابط ، تقوم مؤشرات الترابط الأخرى باستدعاء طريقة start () للكائن لبدء تشغيل مؤشر الترابط. على سبيل المثال ، thread.start (). قد يتم جدولة موضوع في حالة جاهزة للتنفيذ بواسطة وحدة المعالجة المركزية في أي وقت.
3. حالة التشغيل (الجري): يحصل مؤشر الترابط على أذونات وحدة المعالجة المركزية للتنفيذ. تجدر الإشارة إلى أن المواضيع يمكنها فقط إدخال حالة التشغيل من الحالة الجاهزة.
4. الحالة المحظورة: تعني الحالة المحظورة أن الخيط يتخلى عن حقوق استخدام وحدة المعالجة المركزية لسبب ما ويتوقف مؤقتًا عن التشغيل. ليس حتى يدخل الخيط إلى الحالة الجاهزة أن لديها فرصة للذهاب إلى حالة الجري. هناك ثلاثة أنواع من الانسداد:
(01) في انتظار حظر - عن طريق استدعاء طريقة Wait () للاطلاع على الموضوع ، دع موضوع الانتظار حتى الانتهاء من عمل معين.
(02) الحظر المتزامن-فشل مؤشر ترابط في الحصول على قفل المزامنة المتزامنة (نظرًا لأن القفل يشغله مؤشرات ترابط أخرى) ، فسوف يدخل حالة حظر متزامنة.
(03) الحظر الآخر-سيدخل الخيط حالة حظر عن طريق الاتصال بـ Sleep () أو الانضمام () من الموضوع أو إصدار طلب I/O. عندما تم توقيت Sleep () ، انتظرت الانضمام () لإنهاء الخيط أو توقيته ، أو تم الانتهاء من معالجة الإدخال/الإخراج ، حيث تم إعادة إدخال الخيط إلى الحالة الجاهزة.
5. الحالة الميتة: انتهى الخيط من تنفيذ أو الخروج من طريقة Run () بسبب استثناء ، وينهي الخيط دورة حياته.
قم بتنفيذ سلسلة أساليب متعددة الخيوط و Runnable
الموضوع: ورث فئة مؤشر الترابط ، وتنفيذ طريقة التشغيل ، واستدعاء طريقة البدء في الوظيفة الرئيسية لبدء مؤشر الترابط
Runnable: واجهة ، وتنفيذ الواجهة القابلة للتشغيل ، وتمررها كمعلمة إلى مُنشئ مؤشرات الترابط ، وتدعو طريقة البدء في الرئيسية
مثال:
تنفذ مهمة الفئة Runnable {private int ticket = 10 ؛ Override public void run () {for (int i = 0 ؛ i <20 ؛ i ++) {if (this.ticket> 0) {system.out.println (thread.currentThRead (). getName () + "تم بيع التذاكر" + this.ticket--) ؛ }}}}}} ؛ الفئة العامة RunNableTest {public static void main (string [] args) {task mytask = new task () ؛ الموضوع T1 = موضوع جديد (myTask) ؛ الموضوع T2 = موضوع جديد (myTask) ؛ الموضوع T3 = موضوع جديد (myTask) ؛ t1.start () ؛ t2.start () ؛ t3.start () ؛ }} // threadtest.java code code class myThread يمتد thread {private int ticket = 10 ؛ public void run () {for (int i = 0 ؛ i <20 ؛ i ++) {if (this.ticket> 0) {system.out.println (this.getName () + "ticket sell: ticket" + this.ticket--) ؛ }}}} threadtest {public static void main (string [] args) {// start 3 threads t1 ، t2 ، t3 ؛ كل موضوع يبيع 10 تذاكر لكل منهما! mythread t1 = new MyThread () ؛ mythread t2 = new MyThread () ؛ mythread t3 = new MyThread () ؛ t1.start () ؛ t2.start () ؛ t3.start () ؛ }} ؛
الفرق بين الخيط والركض
الموضوع هو فئة ، و Runnable هو واجهة. الموضوع نفسه هو فئة تنفذ الواجهة القابلة للتشغيل. نحن نعلم أن "الفئة يمكن أن تحتوي فقط على فئة الوالدين واحدة ، ولكن يمكنها تنفيذ واجهات متعددة" ، لذلك Runnable لديها قابلية التوسع بشكل أفضل. بالإضافة إلى ذلك ، يمكن أيضًا استخدام Runnable ل "مشاركة الموارد". أي أنه يتم إنشاء مؤشرات ترابط متعددة استنادًا إلى كائن معين قابل للتشغيل ، وسيشاركون الموارد على الكائن القابل للتشغيل. بشكل عام ، يوصى بتنفيذ متعدد الخيوط من خلال "Runnable"!
تشغيل الموضوع والبدء
Start (): وظيفتها هي بدء مؤشر ترابط جديد ، وسيقوم مؤشر الترابط الجديد بتنفيذ طريقة Run () المقابلة. Start () لا يمكن استدعاؤها مرارًا وتكرارًا. ابدأ () يبدأ في الواقع الخيط من خلال الطريقة المحلية Start0 (). ستقوم Start0 () بتشغيل سلسلة رسائل جديدة ، وسيقوم مؤشر الترابط الجديد باستدعاء طريقة Run ().
Run (): يمكن استدعاء Run () مرارًا وتكرارًا مثل أساليب الأعضاء العادية. إذا قمت بالاتصال بـ Run () بشكل منفصل ، فسيتم تنفيذ Run () في مؤشر الترابط الحالي ، ولن يتم بدء تشغيل الخيط الجديد! Run () هو استدعاء طريقة Run () مباشرة للعضو القابل للتشغيل في مؤشر ترابط ، ولن يقوم بإنشاء مؤشر ترابط جديد.
. } public void run () {system.out.println (thread.currentThread (). getName ()+"is re rem") ؛ }} ؛ demo class public {public static void main (string [] args) {thread mythread = new MyThread ("mythread") ؛ System.out.println (thread.currentThRead (). getName ()+"Call MyThread.Run ()") ؛ mythread.run () ؛ System.out.println (thread.currentThRead (). getName ()+"Call MyThread.Start ()") ؛ mythread.start () ؛ }}الإخراج:
Main Call MyThread.run () Main هو RunningMain Call MyThread.Start () MyThread قيد التشغيل
متزامن
في Java ، كل كائن لديه قفل المزامنة. عندما نسمي الطريقة المتزامنة للكائن ، يتم الحصول على قفل الكائن ، ويحصل متزامن (OBJ) على قفل المزامنة "كائن OBJ". وصول مؤشرات الترابط المختلفة إلى قفل التزامن هو حصري بشكل متبادل. لا يمكن الحصول على قفل التزامن للكائن إلا بواسطة مؤشر ترابط واحد في وقت معين. من خلال قفل التزامن ، يمكننا تحقيق الوصول الحصري بشكل متبادل إلى "الكائن/الطريقة" في مؤشرات الترابط المتعددة. على سبيل المثال ، يوجد الآن موضوعان A و Thread B ، والذي سيصل جميعًا إلى "قفل متزامن للكائن OBJ". لنفترض أنه في مرحلة ما ، يكتسب Thread A "قفل مزامنة OBJ" ويؤدي بعض العمليات ؛ في هذا الوقت ، يحاول الخيط B أيضًا الحصول على "قفل مزامنة OBJ" - سيفشل الخيط B في الحصول عليه ، ويجب أن ينتظر حتى يتم تصوير "قفل مزامنة OBJ" ويمكن تشغيله فقط.
القواعد الأساسية
المادة 1: عندما يصل مؤشر ترابط إلى "الطريقة المتزامنة" أو "كتلة رمز متزامنة" من "كائن معين" ، سيتم حظر مؤشرات ترابط أخرى من الوصول إلى "الطريقة المتزامنة" أو "كتلة التعليمات البرمجية المتزامنة" من "الكائن".
المادة 2: عندما يصل مؤشر ترابط إلى "الطريقة المتزامنة" أو "كتلة التعليمات البرمجية المتزامنة" من "كائن معين" ، لا يزال بإمكان مؤشرات الترابط الأخرى الوصول إلى كتلة الكود غير المتزامن من "هذا الكائن".
المادة 3: عندما يصل مؤشر ترابط إلى "الطريقة المتزامنة" أو "كتلة رمز متزامنة" من "كائن معين" ، سيتم منع مؤشرات ترابط أخرى من الوصول إلى "طرق متزامنة أخرى" أو "كتلة رمز متزامنة" من "الكائن".
طريقة متزامنة
proid void foo1 () {system.out.println ("طريقة متزامنة") ؛} كتلة رمز متزامنة الفراغ العام foo2 () {synchronized (this) {system.out.println ("methodized method") ؛ }}يشير هذا في كتلة الكود المتزامن إلى الكائن الحالي. يمكن استبدال هذا أيضًا بكائنات أخرى ، مثل هذا يتم استبداله بـ OBJ ، ثم يكتسب FOO2 () قفل مزامنة OBJ عند مزامنة (OBJ).
يمكن أن تتحكم كتل التعليمات البرمجية المتزامنة في مناطق الوصول المقيدة للصراع بدقة أكبر ، وأحيانًا تؤدي بشكل أكثر كفاءة
قفل الحالة والقفل العالمي
مثيل قفل-محفور على كائن مثيل. إذا كان الفصل مفردة ، فإن القفل لديه أيضًا مفهوم قفل عالمي. تتوافق الكلمة الرئيسية المتزامنة مع قفل المثيل.
القفل العالمي- يستهدف هذا القفل فئة. بغض النظر عن عدد الكائنات على سبيل المثال ، تشترك مؤشرات الترابط في القفل. يتوافق القفل العالمي مع متزامن ثابت (أو مغلق على فئة أو كائن تحميل الفصل في هذه الفئة).
الفئة pulbic شيء {public synchronized void issynca () {} issyncb issyncb () {}
(01) X.issynca () و X.issyncb () لا يمكن الوصول إليها في وقت واحد. لأن issynca () و issyncb () كلاهما أقفال المزامنة التي تصل إلى نفس الكائن (الكائن x)!
(02) x.issynca () و y.issynca () يمكن الوصول إليها في نفس الوقت. نظرًا لأنه لا يصل إلى قفل المزامنة لنفس الكائن ، فإن X.issynca () تصل إلى قفل المزامنة لـ x ، بينما يصل y.issynca () إلى قفل التزامن y.
(03) X.CSYNCA () و Y.CSYNCB () لا يمكن الوصول إليها في وقت واحد. نظرًا لأن csynca () و csyncb () كلاهما نوعان ثابتان ، فإن x.csynca () يعادل شيئًا. sissynca () ، و y.csyncb () يعادل شيئًا.
(04) x.issynca () ويمكن الوصول إلى شيء. csynca () في وقت واحد. لأن Issynca () طريقة مثيل ، يستخدم X.issynca () قفل الكائن X ؛ على الرغم من أن Csynca () هي طريقة ثابتة ، إلا أن شيئًا ما. csynca () يمكن أن يفهم أنه "قفل فئة" مستخدم. لذلك ، يمكن الوصول إليها في وقت واحد.
حظر الخيط والاستيقاظ الانتظار ، والإخطار ، والإخطار
في Object.java ، يتم تعريف واجهات مثل Wait () ، و Amply () وإخطار (). تتمثل وظيفة WAIT () في السماح لخيط الخيط الحالي بإدخال حالة انتظار ، وسيسمح WAIT () أيضًا بتصدر مؤشر الترابط الحالي القفل الذي يحتفظ به. دور الإخطار () وإخطار () هو استيقاظ مؤشر ترابط الانتظار على الكائن الحالي ؛ الإخطار () هو استيقاظ موضوع واحد ، في حين أن الإخطار () هو إيقاظ جميع المواضيع.
تفاصيل API حول الانتظار/الاستيقاظ في فئة الكائن هي كما يلي:
إخطار () - استيقظ مؤشر ترابط واحد ينتظر على شاشة الكائن هذا.
الإخطار () - استيقظ على جميع المواضيع التي تنتظر شاشة الكائن هذا.
انتظر () - ضع مؤشر الترابط الحالي في حالة "انتظر (حظر)" و "حتى تتصل مؤشرات الترابط الأخرى بطريقة الإخطار () أو طريقة الإخطار () لهذا الكائن" ، ويتم إيقاظ مؤشر الترابط الحالي (تم إدخاله إلى "الحالة الجاهزة").
انتظر (فترة طويلة) - ضع الخيط الحالي في حالة "انتظار (حظر)" و "حتى تتصل مؤشرات الترابط الأخرى بأسلوب الكائن () أو طريقة الإخطار () ، أو يتجاوز مقدار الوقت المحدد" ، ويتم استيقاظ الخيط الحالي (تم إدخاله إلى "الحالة الجاهزة").
انتظر (Timeout الطويل ، int nanos) - دع الخيط الحالي يكون في "حالة الانتظار (حظر)" ، حتى يتصل مؤشر ترابط آخر بالطريقة () الإخطار () أو طريقة الإخطار () ، أو بعض خيط آخر يقطع مؤشر الترابط الحالي ، أو تجاوز قدرًا معينًا من الوقت "، ويتم استيقاظ مؤشر الترابط الحالي (تم إدخاله إلى" الحالة الجاهزة ").
. } public void run () {Synchronized (this) {system.out.println (thread.currentThRead (). getName ()+"call call notify ()") ؛ // استيقظ مؤشر ترابط الانتظار الحالي () ؛ }}} الفئة العامة waittest {public static void main (string [] args) {threada t1 = new threada ("t1") ؛ Synchronized (t1) {try {// start "thread t1" system.out.println (thread.currentThRead (). getName ()+"start t1") ؛ t1.start () ؛ // ينتظر الخيط الرئيسي T1 للاستيقاظ من خلال الإخطار (). System.out.println (thread.currentThRead (). getName ()+"wait ()") ؛ T1.WAIT () ؛ System.out.println (thread.currentThRead (). getName ()+"متابعة") ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}}}الإخراج
البداية الرئيسية T1Main Wait () T1 Call Notify () Main Conter
(01) لاحظ أن "الخيط الرئيسي" في الشكل يمثل "مؤشر الترابط الرئيسي الرئيسي". "الموضوع T1" يمثل "الموضوع T1" بدأ في Waittest. و "القفل" يمثل "قفل متزامن للكائن T1".
(02) "الخيط الرئيسي" ينشئ "موضوع T1" جديد من خلال Threada جديد ("T1"). ثم ، يتم الحصول على "القفل المتزامن لكائن T1" من خلال متزامن (T1). ثم اتصل بـ T1.start () لبدء "Thread T1".
(03) ينفذ "مؤشر الترابط الرئيسي" T1.WAIT () لإطلاق "قفل كائن T1" ويدخل "Wait (حظر) الحالة". انتظر المواضيع على كائنات T1 لتستيقظها عبر إخطار () أو إخطار ().
(04) بعد تشغيل "Thread T1" ، يتم الحصول على "قفل الكائن الحالي" من خلال متزامن (هذا) ؛ ثم اتصل بـ Notify () لإيقاظ "مؤشر ترابط الانتظار على الكائن الحالي" ، أي استيقظ على "الخيط الرئيسي".
(05) بعد اكتمال "Thread T1" ، حرر "قفل الكائن الحالي". بعد ذلك مباشرة ، يكتسب "الخيط الرئيسي" "قفل كائن T1" ثم يعمل.
T1.WAIT () هي طريقة WAIT () تسمى من خلال "Thread T1" ، ولكن المكان الذي يسمى t1.wait () في "Main Thread Main". يجب أن يكون الخيط الرئيسي هو "الخيط الحالي" ، أي حالة التشغيل ، قبل تنفيذ t1.wait (). لذلك ، "الخيط الحالي" في هذا الوقت هو "الموضوع الرئيسي الرئيسي"! لذلك ، T1.WAIT () هو جعل "الخيط الرئيسي" انتظر ، وليس "الموضوع T1"!
package thread.test ؛ الفئة العامة etrifyalltest {private static object obj = new Object () ؛ public static void main (string [] args) {threada t1 = new threada ("t1") ؛ threada t2 = new threada ("t2") ؛ threada t3 = new threada ("t3") ؛ t1.start () ؛ t2.start () ؛ t3.start () ؛ حاول {system.out.println (thread.currentThRead (). getName ()+"Sleep (3000)") ؛ thread.sleep (3000) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } synchronized (obj) {system.out.println (thread.currentThRead (). getName ()+"eletifyall ()") ؛ obj.notifyall () ؛ // wake up } public void run () {synchronized (obj) {try {// printout result system.out.println (thread.currentTherAd (). getName () + "wait") ؛ // refer the OBJ Object Lock OBJ.WAIT () ؛ // printout result system.out.println (thread.currentThRead (). getName () + "conte") ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}}}}} الإخراج:
T1 Waitmain Sleep (3000) T3 WAITT2 WAITMAIN NOTIFELALL () T2 تابع 3 متابعة 1 متابعة
(01) تم إنشاء المواضيع 3 "T1" و "T2" و "T3" وبدأت في الخيط الرئيسي.
(02) ينام الخيط الرئيسي لمدة 3 ثوان من خلال النوم (3000). خلال نوم الخيط الرئيسي لمدة 3 ثوان ، نفترض أن الخيوط الثلاثة "T1" و "T2" و "T3" تعمل كلها. خذ "T1" كمثال. عند تشغيله ، سيتم تنفيذ OBJ.WAIT () لانتظار خيوط أخرى لتستيقظها من خلال الإخطار () أو nofityall () ؛ على نفس المنوال ، سينتظر "T2" و "T3" أيضًا أن توقظها مؤشرات ترابط أخرى من خلال nofity () أو nofityall ().
(03) ينام الخيط الرئيسي لمدة 3 ثوان ثم يعمل. قم بتنفيذ OBJ.Notifyall () لإيقاظ موضوع الانتظار على OBJ ، أي ، استيقظ الخيوط الثلاثة "T1" و "T2" و "T3". مباشرة بعد تشغيل متزامن الخيط الرئيسي (OBJ) ، يطلق الخيط الرئيسي "قفل OBJ". وبهذه الطريقة ، يمكن "T1" و "T2" و "T3" الحصول على "قفل OBJ" والاستمرار في الجري!
إخطار العلاقة وإخطارها وقفل
ستعمل وظائف مثل WAIT () ، الإخطار () في الكائن ، مثل المزامنة ، على "قفل مزامنة الكائن".
انتظر () سيجعل "الخيط الحالي" انتظر. نظرًا لأن الخيط يدخل حالة الانتظار ، يجب أن يطلق الخيط "القفل المتزامن" الذي يحتفظ به قفله ، وإلا فلن تتمكن المواضيع الأخرى من الحصول على "القفل المتزامن" ولن يكون قادرًا على التشغيل!
حسنًا ، بعد مكالمات الخيط WAIT () ، ستصدر "القفل المتزامن" الذي يحتفظ به قفله ؛ ووفقًا للمقدمة السابقة ، فإننا نعلم أنه يمكن إيقاظ موضوع الانتظار بإخطار () أو إخطار (). الآن ، يرجى التفكير في سؤال: ما هو الإخطار () بناءً على إيقاظ موضوع الانتظار؟ أو ، ما هو العلاقة بين Wait () وإخطار ()؟ الجواب هو: بناءً على "قفل مزامنة الكائن".
لا يمكن للمعلومات المسؤولة عن إيقاظ مؤشر ترابط الانتظار (نسميه "خيط الاستيقاظ") ، فقط إيقاظ مؤشر ترابط الانتظار بعد الحصول على "القفل المتزامن لهذا الكائن" (يجب أن يكون قفل المزامنة هنا هو نفس قفل المزامنة لخيط الانتظار) واتصالات الإخطار () أو طرق () (). على الرغم من أن موضوع الانتظار يستيقظ ؛ ومع ذلك ، لا يمكن تنفيذها على الفور لأن مؤشر ترابط الاستيقاظ لا يزال يحمل "قفل متزامن للكائن". يجب أن تنتظر حتى يطلق مؤشر ترابط الاستيقاظ "قفل مزامنة الكائن" قبل أن تتمكن من الحصول على "قفل مزامنة الكائن" والاستمرار في التشغيل.
باختصار ، يعتمد الإخطار () ، WAIT () على "قفل متزامن" ، والذي يتم الاحتفاظ به بواسطة أقفال الكائن ، وكل كائن لديه واحد فقط! هذا هو السبب في تعريف وظائف مثل الإخطار () ، WAIT () في فئة الكائن ، وليس في فئة مؤشرات الترابط.
تنازلات الموضوع العائد
تجعل تنازلات مؤشرات الترابط تغيير مؤشر الترابط من حالة التنفيذ إلى الحالة الجاهزة ، بحيث يمكن لخيوط انتظار أخرى ذات نفس الأولوية الحصول على حقوق التنفيذ ؛ ومع ذلك ، لا يضمن أنه بعد العائد على المكالمات الحالية () ، ستحصل مؤشرات الترابط الأخرى ذات الأولوية نفسها على حقوق التنفيذ ؛ من الممكن أيضًا أن يدخل الخيط الحالي "حالة الجري" ويستمر في التشغيل.
الخضوع وانتظر
(01) WAIT () هو السماح لخيط مؤشر الترابط بإدخال "Wait (حظر) حالة" من "حالة الجري" ، في حين أن عدم العائد () هو السماح لخيط الدخول إلى "الحالة الجاهزة" من "الحالة الجارية".
(02) WAIT () هو قفل المزامنة سيؤدي إلى تحرير الكائن الذي يحتفظ به ، في حين أن طريقة العائد () لن تطلق القفل.
(03) الانتظار هو طريقة الكائن ، العائد هو طريقة مؤشر الترابط
موضوع النوم
تتمثل وظيفة النوم () في السماح للخيط الحالي بالنوم ، أي أن الخيط الحالي سوف يدخل من "حالة الجري" إلى "حالة النوم (الحظر)". سيحدد النوم () وقت النوم ، وسيكون وقت النوم في الخيط أكبر من وقت النوم ؛ عندما يتم إيقاظ الخيط مرة أخرى ، سيتغير من "حالة حظر" إلى "حالة جاهزة" ، في انتظار موعد تنفيذ وحدة المعالجة المركزية.
الفرق بين النوم والانتظار
تتمثل وظيفة WAIT () في السماح لخيط الخيط الحالي بدخول حالة "الانتظار (حظر) من" حالة الجري "وأيضًا إطلاق قفل المزامنة. تتمثل وظيفة النوم () في السماح لخيط الخيط الحالي بإدخال" حالة النوم (الحظر) من "الحالة الجارية". (هذا في الواقع لا يختلف كثيرا)
انتظر () يطلق قفل تزامن الكائن ، في حين أن Sleep () لا يطلق القفل
الانتظار هي طريقة الكائن ، والنوم هو طريقة الخيط
ينضم
دع الخيط الرئيسي ينتظر ، ويمكن أن يستمر سلسلة خيط الطفل بعد اكتمال الخيط الرئيسي.
مقاطعة
تستخدم لإنهاء موضوع محظور
Overridepublic void run () {try {بينما (صحيح) {// قم بتنفيذ المهمة ...}} catch (interruptedException ie) {// بسبب استثناء interruptedException ، قم بالخروج من الحلقة (True) وترسل سلسلة الرسائل! }}في حين (صحيح) ، فإن دعوة المقاطعة () من الخيط توليد مقاطعة مقاطعة. الالتقاط المتقطع خارج في حين (صحيح) ، وبالتالي الخروج من الحلقة (الحقيقية)
ينهي موضوع في حالة الجري
Overridepublic void Run () {بينما (! isInterrupted ()) {// تنفيذ المهمة ...}}طريقة شائعة لإنهاء المواضيع
Overridepublic void Run () {try {// 1. isInterrupted () يضمن إنهاء مؤشر الترابط طالما تم وضع علامة على المقاطعة. بينما (! isInterrupted ()) {// تنفيذ المهمة ...}} catch (interruptedException ie) {// 2. استثناء InterruptedException يضمن أنه عند حدوث استثناء مقاطع ، يتم إنهاء مؤشر الترابط. }}
أولوية الموضوع
نطاق أولوية مؤشر الترابط في Java هو 1 إلى 10 ، والأولوية الافتراضية هي 5. هناك نوعان من المواضيع في جافا: مؤشر ترابط المستخدم وخيط الخفي. يمكن تمييزها بواسطة طريقة Isdaemon (): إذا تم إرجاع خطأ ، فهذا يعني أن مؤشر الترابط هو "مؤشر ترابط المستخدم" ؛ وإلا فهو "خيط الخفي". تؤدي مؤشرات ترابط المستخدم عمومًا مهام مستوى المستخدم ، في حين أن مؤشرات الترابط الخفي هي أيضًا "مؤشرات ترابط الخلفية" ، والتي تستخدم عمومًا لأداء مهام الخلفية. تجدر الإشارة إلى أن جهاز Java Virtual سيخرج بعد اكتمال "مؤشر ترابط المستخدم".
كل موضوع له أولوية. "الخيوط ذات الأولوية العالية" ستسبق التنفيذ على "مؤشرات الترابط ذات الأولوية المنخفضة". يمكن تمييز كل موضوع كخفي أو غير دومون. عند إنشاء مؤشر ترابط طفل جديد في بعض الخيط الرئيسي الذي يعمل ، يتم تعيين أولوية مؤشر ترابط الطفل على "أولوية الخيط الرئيسي الذي أنشأته" ، و "خيط الطفل سيكون الخيط الخفي" متى وفقط "الخيط الرئيسي الذي تم إنشاؤه هو خيط خفي".
عند بدء تشغيل جهاز Java Virtual ، عادة ما يكون هناك مؤشر ترابط واحد غير Daemon (يتم تشغيل هذا الموضوع من خلال الطريقة الرئيسية ()). سيتم تشغيل JVM حتى يحدث أي من الشروط التالية ، وسيقوم JVM بإنهاء التشغيل:
(01) يتم استدعاء طريقة الخروج () ، والخروج () لديه إذن لتنفيذه بشكل طبيعي.
(02) جميع "الخيوط غير الدائرية" قد ماتت (أي ، هناك فقط "خيوط Daemon" في JVM).
شيطان
(01) المرشح الرئيسي الرئيسي هو مؤشر ترابط المستخدم ، وخيط الطفل T1 الذي ينشئه هو أيضًا مؤشر ترابط المستخدم.
(02) T2 هو موضوع الخفي. عند تنفيذ "الخيط الرئيسي الرئيسي" و "Tran-thread T1" (كلاهما مؤشرات ترابط المستخدم) ، يتم ترك مؤشر ترابط Daemon T2 فقط ، ويخرج JVM تلقائيًا.
قضايا المنتج والمستهلك
(01) ينتج المنتج فقط عندما لا يكون المستودع ممتلئًا ، ويتوقف عن الإنتاج عندما يكون المستودع ممتلئًا.
(02) يمكن للمستهلكين الاستهلاك فقط عندما يكون لديهم منتجات في التخزين ، والانتظار إذا كان لديهم مستودعات فارغة.
(03) عندما يجد المستهلكون أنه لا يوجد منتج للاستهلاك في المستودع ، فإنهم سوف يخطرون المنتج.
(04) عندما ينتج المنتجون منتجات قابلة للاستهلاك ، يجب عليهم إخطار المستهلكين للانتظار للاستهلاك.
التواصل بين المواضيع
الطريقة: الذاكرة المشتركة والرسائل
الذاكرة المشتركة: مؤشر الترابط A و Thread B مشاركة الذاكرة ، ويقوم مؤشر ترابط A بتحديث قيمة المتغير المشترك ، ويقوم بتحديثها إلى الذاكرة الرئيسية ، وينتقل مؤشر الترابط B إلى الذاكرة الرئيسية لقراءة المتغيرات المحدثة من مؤشر الترابط A. يجب أن تمر عملية الاتصال بأكملها عبر الذاكرة الرئيسية. يتم تنفيذ التزامن بشكل صريح.
إذا كان المتغير من النوع المتقلب ، فستكون قراءة وكتابة المتغير ذريًا. إذا كانت عمليات متعددة متقلبة أو عمليات مركبة مشابهة لـ Folatile ++ ، فإن هذه العمليات ليست ذرية في مجملها.
المتغير المتطاير نفسه له الخصائص التالية:
[الرؤية]: عند قراءة متغير متطاير ، يمكنك دائمًا رؤية الكتابة الأخيرة إلى المتغير المتقلبة (أي مؤشر ترابط).
[Atomicity]: لديها ذرة لقراءة/كتابة أي متغير متطاير واحد ، ولكن ليس لديه ذرة لعمليات مركبة مماثلة للتقلب ++.
الكتابة المتطايرة: عند كتابة متغير متطاير ، ستقوم JMM بتطهير المتغير المشترك في الذاكرة المحلية المقابلة للمعلومات إلى الذاكرة الرئيسية.
قراءة متقلبة: عند قراءة متغير متطاير ، ستقوم JMM بإبطال الذاكرة المحلية المقابلة للمعلومات. سيقوم مؤشر الترابط بعد ذلك بقراءة المتغير المشترك من الذاكرة الرئيسية.
تسليم الرسائل: يتم تنفيذ إرسال رسالة ضمنيًا قبل قبول الرسالة.
threadlocal
لا يتم استخدام ThreadLocal لحل مشكلة الوصول متعدد الخيوط إلى الكائنات المشتركة. بشكل عام ، الكائن إلى مؤشر الترابط من خلال threadlocal.set () هو كائن يستخدمه مؤشر الترابط نفسه. لا تحتاج إلى الوصول إلى المواضيع الأخرى ولا يمكن الوصول إليها. يسمح ThreadLocal لكل مؤشر ترابط بالحفاظ على كائنه المستقل. لا يتم تنفيذه من خلال threadlocal.set () ، ولكن كائن تم إنشاؤه بواسطة تشغيل الكائن الجديد في كل مؤشر ترابط. كل مؤشر ترابط ينشئ واحد ، وليس نسخة أو نسخة من الكائن. يتم حفظ الإشارة إلى الكائن الذي تم إنشاؤه حديثًا إلى خريطة كل مؤشر ترابط من خلال threadlocal.set (). كل موضوع لديه مثل هذه الخريطة. عند تنفيذ threadlocal.get () ، يخرج كل مؤشر ترابط الكائن الموضع من خريطته الخاصة. لذلك ، ما يتم إخراجه هو الكائن في كل مؤشر ترابط. يتم استخدام مثيل ThreadLocal كمفتاح الخريطة. إذا كان الشيء الذي يدخله threadlocal.set () هو نفس الكائن المشترك بواسطة مؤشرات ترابط متعددة ، فإن threadlocal.get () لا يزال يحصل على الكائن المشترك نفسه ، ولا تزال هناك مشكلة وصول متزامنة.
تنفيذ threadlocal
استيراد java.util.collections ؛ استيراد java.util.hashmap ؛ استيراد java.util.map ؛ /** * class باستخدام threadlocal * * author leizhimin 2010-1-5 10:35:27 */public class myThreadLocal {// تحديد متغير threadlocal لحفظ int أو integer private com.lavasoft.test2.ThreadLocal <Steger> tl = new com عدد صحيح initialValue () {return 0 ؛ }} ؛ integer getNextNum () {// احصل على قيمة TL وإضافة 1 ، وتحديث قيمة T1 tl.set (tl.get () + 1) ؛ إرجاع tl.get () ؛ }} class threadlocal <T> {private map <froof ، t> map = collections.synchronizedMap (new hashmap <froof ، t> ()) ؛ public threadlocal () {} محمية t initialValue () {return null ؛ } public t get () {thread t = thread.currentThRead () ؛ t obj = map.get (t) ؛ if (obj == null &&! map.containskey (t)) {obj = initialValue () ؛ map.put (t ، obj) ؛ } إرجاع OBJ ؛ } مجموعة void العامة (قيمة t) {map.put (thread.currentThread () ، value) ؛ } public void remove () {map.remove (thread.currentThread ()) ؛ }}في الواقع ، Threadlocal يفعل هذا:
public t get () {thread t = thread.currentThRead () ؛ threadlocalmap خريطة = getMap (t) ؛ if (map! = null) {threadlocalmap.entry e = map.getentry (this) ؛ if (e! = null) return (t) e.value ؛ } return setInitialValue () ؛ }