جدول المحتويات (؟) [-]
أحدهما يمتد إلى JavalangThread Class 2 ينفذ واجهة JavalAngrunnable ثلاث اختلافات بين مؤشر الترابط و Runnable Four Thread Transition
تتحدث هذه المقالة بشكل أساسي عن طرق استخدام الخيوط المتعددة في Java ، ومزامنة مؤشرات الترابط ، ونقل بيانات مؤشرات الترابط ، وحالة مؤشر الترابط واستخدام وظيفة مؤشر الترابط المقابلة ونظرة عامة.
أولاً ، دعنا نتحدث عن الفرق بين العملية والموضوع:
العملية: كل عملية لها رمز مستقل ومساحة بيانات (سياق العملية) ، وسيكون للتبديل بين العمليات العامة الكبيرة. تحتوي العملية على مؤشرات ترابط 1-N.
الموضوع: نفس النوع من مؤشرات الترابط مشاركة الكود ومساحة البيانات. يحتوي كل مؤشر ترابط على مكدس تشغيل مستقل وبرنامج (PC) ، وتبديل مؤشر الترابط العلوي صغير.
مثل العملية ، يتم تقسيم المواضيع إلى خمس مراحل: الخلق ، الجري ، الجري ، الحظر ، وإنهاء.
تعني العمليات المتعددة أن نظام التشغيل يمكنه تشغيل مهام متعددة (برامج) في نفس الوقت.
يشير Multithreading إلى تدفقات متسلسلة متعددة يتم تنفيذها في نفس البرنامج.
في Java ، هناك طريقتان لتنفيذ متعدد الخيوط. أحدهما هو متابعة فئة الخيوط ، والآخر هو تنفيذ الواجهة القابلة للتشغيل.
1. تمديد فئة java.lang.thread
package com.multithread.learning ؛/***@functon multiThReading Learning*@efull lin bingwen*@time 2015.3.9*/class thread1 يمتد thread {اسم السلسلة الخاصة ؛ public thread1 (اسم السلسلة) {this.name = name ؛ } public void run () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (name + "run:" + i) ؛ جرب {Sleep ((int) Math.Random () * 10) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}}} الفئة العامة الرئيسية {public static void main (string [] args) {thread1 mth1 = new thread1 ("a") ؛ thread1 mth2 = new thread1 ("b") ؛ mth1.start () ؛ mth2.start () ؛ }} الإخراج:
تشغيل: 0
ب المدى: 0
تشغيل: 1
تشغيل: 2
تشغيل: 3
تشغيل: 4
ب المدى: 1
ب المدى: 2
ب المدى: 3
ب المدى: 4
قم بتشغيله مرة أخرى:
تشغيل: 0
ب المدى: 0
ب المدى: 1
ب المدى: 2
ب المدى: 3
ب المدى: 4
تشغيل: 1
تشغيل: 2
تشغيل: 3
تشغيل: 4
يوضح:
عندما يبدأ البرنامج ويعمل بشكل رئيسي ، يبدأ جهاز Java Virtual عملية ، ويتم إنشاء مؤشر الترابط الرئيسي عندما يتم استدعاء Main (). مع طريقة البدء للكائنين من Mitisay ، يتم أيضًا بدء تشغيل الخيوط الأخرى ، بحيث يتم تشغيل التطبيق بأكمله تحت مؤشرات ترابط متعددة.
ملاحظة: تسمى طريقة START () لا لتنفيذ التعليمات البرمجية متعددة الخيوط على الفور ، ولكن بدلاً من ذلك يجعل مؤشر الترابط يصبح حالة قابلة للتشغيل. عندما يتم تشغيله يتم تحديده بواسطة نظام التشغيل.
من نتائج تشغيل البرنامج ، يمكننا أن نجد أن البرامج متعددة الخيوط يتم تنفيذها خارج الترتيب. لذلك ، يجب تصميم الرمز الذي تم تنفيذه فقط خارج الترتيب على أنه متعدد الخيوط.
الغرض من thread.sleep () الاحتجاج هو منع مؤشر الترابط الحالي من احتلال موارد وحدة المعالجة المركزية التي تم الحصول عليها من قبل العملية وحدها ، وذلك لترك قدر معين من الوقت لتنفيذ مؤشرات الترابط الأخرى.
في الواقع ، فإن ترتيب تنفيذ جميع الكود متعدد الخيوط غير مؤكد ، ونتائج كل تنفيذ عشوائية.
ومع ذلك ، إذا تم استدعاء طريقة البدء بشكل متكرر ، فسيحدث java.lang.illegalthreadStateException.
thread1 mth1 = new thread1 ("a") ؛ thread1 mth2 = mth1 ؛ mth1.start () ؛ mth2.start () ؛ الإخراج:
استثناء في الموضوع "الرئيسي" java.lang.illegalthreadStateException
في java.lang.thread.start (مصدر غير معروف)
في com.multithread.learning.main.main (main.java:31)
تشغيل: 0
تشغيل: 1
تشغيل: 2
تشغيل: 3
تشغيل: 4
2. تنفيذ واجهة java.lang.runnable
/***@functon multiThReading Learning*@Author Lin Bingwen*@time 2015.3.9*/package com.multithread.runnable ؛ class thread2 تنفذ runnable {private string name ؛ public thread2 (اسم السلسلة) {this.name = name ؛ } Override public void run () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (name + "run:" + i) ؛ حاول {thread.sleep ((int) math.random () * 10) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}}} الفئة العامة الرئيسية {public static void main (string [] args) {new thread (new thread2 ("c"). Start () ؛ موضوع جديد (Thread2 جديد ("D")). start () ؛ }} الإخراج:
C Run: 0
D Run: 0
D Run: 1
C Run: 1
D Run: 2
C Run: 2
D Run: 3
C Run: 3
D Run: 4
C Run: 4
يوضح:
تقوم فئة Thread2 بتنفيذ واجهة Runnable ، والتي تجعل الفئة لها خصائص فئة متعددة الخيوط. طريقة Run () هي اتفاقية للبرامج المتعددة. جميع التعليمات البرمجية متعددة الخيوط في طريقة التشغيل. فئة الخيط هي في الواقع فئة تنفذ الواجهة القابلة للتشغيل.
عند بدء تشغيل متعدد الخيوط ، تحتاج أولاً إلى إنشاء الكائن من خلال مؤشر ترابط فئة مؤشرات الترابط (الهدف القابل للتشغيل) ، ثم استدعاء طريقة START () لكائن مؤشر الترابط لتشغيل الرمز متعدد الخيوط.
في الواقع ، يتم تشغيل جميع التعليمات البرمجية متعددة التربعات من خلال تشغيل طريقة تشغيل () ترابط. لذلك ، سواء كان ذلك هو تمديد فئة مؤشرات الترابط أو تنفيذ الواجهة القابلة للتشغيل لتنفيذ متعدد الخيوط ، أو التحكم في نهاية المطاف من خلال واجهة برمجة تطبيقات كائن الخيط ، فإن التعرف على واجهة برمجة تطبيقات فئة مؤشرات الترابط هو أساس البرمجة متعددة الخيوط.
3. الفرق بين الخيط والركض
إذا ورث الفئة موضوعًا ، فهذا غير مناسب لمشاركة الموارد. ومع ذلك ، إذا تم تنفيذ الواجهة القابلة للتشغيل ، فمن السهل تنفيذ مشاركة الموارد.
Package com.multithread.learning ؛/***@Functon Multi-thread Learning ، Thread Thread ، لا يمكن مشاركة الموارد*@Author Lin Bingwen*@time 2015.3.9*/Class Thread1 يمتد Thread {private int count = 5 ؛ اسم السلسلة الخاصة ؛ public thread1 (اسم السلسلة) {this.name = name ؛ } public void run () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (name + "run count =" + count--) ؛ جرب {Sleep ((int) Math.Random () * 10) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}}} الفئة العامة الرئيسية {public static void main (string [] args) {thread1 mth1 = new thread1 ("a") ؛ thread1 mth2 = new thread1 ("b") ؛ mth1.start () ؛ mth2.start () ؛ }} الإخراج:
B Run Count = 5
عدد التشغيل = 5
B Run Count = 4
B Run Count = 3
B Run Count = 2
B Run Count = 1
عدد التشغيل = 4
عدد التشغيل = 3
عدد التشغيل = 2
عدد التشغيل = 1
مما سبق ، يمكننا أن نرى أن التهم مختلفة بين المواضيع المختلفة ، والتي سيكون لها مشكلة كبيرة لنظام بيع التذاكر. بالطبع ، يمكن استخدام التزامن هنا. دعونا نستخدم Runnable للقيام بذلك هنا
/***@Functon Multi-Thrheading Learning Runnable ، يمكن مشاركة الموارد*@المؤلف Lin Bingwen*@time 2015.3.9*/package com.multithread.runnable ؛ class thread2 تنفذ runnable {private int count = 15 ؛ Override public void run () {for (int i = 0 ؛ i <5 ؛ i ++) {system.out.println (thread.currentThRead (). getName () + "run count =" + count--) ؛ حاول {thread.sleep ((int) math.random () * 10) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}}}} الفئة العامة الرئيسية {public static void main (string [] args) {thread2 my = new thread2 () ؛ موضوع جديد (بلدي ، "C"). ابدأ () ؛ // نفس MT ، لكنه غير ممكن في الموضوع. إذا قمت بتثبيت الكائن MT ، فسيظهر استثناء مؤشر ترابط جديد (My ، "D"). Start () ؛ موضوع جديد (بلدي ، "E"). start () ؛ }} الإخراج:
C Run Count = 15
عدد تشغيل D = 14
e تشغيل العد = 13
D Run Count = 12
عدد تشغيل D = 10
D Run Count = 9
D Run Count = 8
C Run Count = 11
e تشغيل العد = 12
C Run Count = 7
e تشغيل العد = 6
C Run Count = 5
e تشغيل العد = 4
C Run Count = 3
e تشغيل العد = 2
هنا يجب أن نلاحظ أن كل مؤشر ترابط يستخدم نفس كائن الاستئصال. إذا لم يكن الأمر نفسه ، فسيكون التأثير هو نفسه أعلاه!
تلخيص:
مزايا تنفيذ الواجهة القابلة للتشغيل على ورث فئة الخيط:
1): مناسبة لمروح مؤشرات ترابط متعددة مع نفس رمز البرنامج لمعالجة نفس المورد
2): يمكن تجنب الحد من الميراث الفردي في جافا
3): زيادة متانة البرنامج ، يمكن مشاركة الكود بواسطة مؤشرات ترابط متعددة ، والرمز والبيانات مستقلة
اسمحوا لي أن أذكرك: الطريقة الرئيسية هي في الواقع موضوع. في Java ، يتم تشغيل المواضيع في نفس الوقت. بالنسبة إلى متى وأي شخص يتم تنفيذه أولاً ، فإنه يعتمد كليا على من يحصل على موارد وحدة المعالجة المركزية أولاً.
في Java ، يتم تشغيل ما لا يقل عن موضوعين في كل مرة يتم تشغيل البرنامج. أحدهما هو الخيط الرئيسي والآخر هو خيط جمع القمامة. لأنه كلما تم تنفيذ فئة باستخدام أوامر Java ، سيتم بدء تشغيل JVM بالفعل ، ويبدأ كل تدريب JVM في نظام التشغيل.
4. انتقال حالة الموضوع
1. حالة جديدة (جديدة): يتم إنشاء كائن مؤشر ترابط جديد.
2. حالة جاهزة (Runnable): بعد إنشاء كائن مؤشر الترابط ، تقوم مؤشرات الترابط الأخرى باستدعاء طريقة START () للكائن. يقع الخيط في هذه الحالة في مجموعة الخيوط القابلة للتشغيل وتصبح قابلة للتشغيل ، في انتظار الحصول على حقوق استخدام وحدة المعالجة المركزية.
3. حالة التشغيل: يحصل مؤشر الترابط في الحالة الجاهزة على وحدة المعالجة المركزية وتنفيذ رمز البرنامج.
4. الحالة المحظورة: تعني الحالة المحظورة أن الخيط يتخلى عن حقوق استخدام وحدة المعالجة المركزية لسبب ما ويتوقف مؤقتًا عن التشغيل. ليس حتى يدخل الخيط إلى الحالة الجاهزة أن لديها فرصة للذهاب إلى حالة الجري. هناك ثلاثة أنواع من الانسداد:
(1) في انتظار الحظر: يقوم مؤشر ترابط التشغيل بتنفيذ طريقة WAIT () ، وسيقوم JVM بوضع الخيط في تجمع الانتظار.
(2) الحظر المتزامن: عندما يكتسب مؤشر ترابط التشغيل قفل مزامنة الكائن ، إذا كان قفل المزامنة يشغله مؤشرات ترابط أخرى ، فإن JVM سيضع الخيط في تجمع القفل.
(3) ، حظر آخر: عندما ينفذ مؤشر ترابط تشغيل Sleep () أو طريقة الانضمام () ، أو يصدر طلب I/O ، سيقوم JVM بتعيين الخيط على حالة الحظر. عندما تم توقيت Sleep () ، انتظرت الانضمام () لإنهاء الخيط أو توقيته ، أو تم الانتهاء من معالجة الإدخال/الإخراج ، حيث تم إعادة إدخال الخيط إلى الحالة الجاهزة.
5. الحالة الميتة: انتهى الخيط من تنفيذ أو الخروج من طريقة Run () بسبب استثناء ، وينهي الخيط دورة حياته.
5. جدولة الموضوع
جدولة الموضوع
1. ضبط أولوية مؤشر الترابط: تتمتع مؤشرات الترابط Java بالأولوية ، وستحصل المواضيع ذات الأولوية العالية على المزيد من الفرص للتشغيل.
يتم تمثيل أولوية خيوط Java بواسطة أعداد صحيحة ، مع نطاق قيمة قدره 1 ~ 10. تحتوي فئة الخيط على الثوابت الثابتة الثلاثة التالية:
ثابت int max_priority
أعلى الأولوية التي يمكن أن يكون لها الخيط 10.
ثابت int min_priority
أدنى أولوية يمكن أن يكون لها الخيط 1.
ثابت int norm_priority
الأولوية الافتراضية المخصصة للموضوع هي 5.
يتم استخدام أساليب setPriority () و getPriority () لفئة الخيط لتحديد أولوية الخيط والحصول عليها على التوالي.
كل مؤشر ترابط له أولوية افتراضية. الأولوية الافتراضية للمعلومات الرئيسية هي thread.norm_priority.
وراثي أولوية المواضيع. على سبيل المثال ، إذا تم إنشاء الخيط B في الموضوع A ، فسيكون لـ B نفس الأولوية مثل A.
يوفر JVM 10 أولويات خيط ، ولكنه لا يخطط بشكل جيد مع أنظمة التشغيل المشتركة. إذا كنت ترغب في نقل البرنامج إلى كل نظام تشغيل ، فيجب عليك فقط استخدام فئة مؤشرات الترابط مع الثوابت الثابتة الثلاثة التالية كأولوية ، والتي يمكن أن تضمن أن نفس الأولوية تعتمد نفس طريقة الجدولة.
2. نوم الخيط: thread.sleep (millis long millis) لجعل الخيط يذهب إلى حالة الحظر. تضع المعلمة Millis وقت النوم بالمللي ثانية. عندما ينتهي النوم ، يصبح القابل للتشغيل. منصة Sleep () لديها قابلية جيدة للنقل.
3. انتظر مؤشر الترابط: تتسبب طريقة WAIT () في فئة الكائن في الانتظار حتى يتم انتظار مؤشرات الترابط الحالية حتى تتصل مؤشرات الترابط الأخرى بطريقة الإخطار () أو طريقة الاستيقاظ. هاتان طريقتان للاستيقاظ هما أيضًا طرق في فئة الكائن ، وسلوكهما يعادل استدعاء الانتظار (0).
4. تنازلات مؤشر الترابط: Thread.yield () تعلق طريقة كائن مؤشر ترابط تنفيذ حاليًا ويعطي فرصة التنفيذ للمواضيع ذات الأولوية نفسها أو أعلى.
5. استدعاء طريقة Join () لخيط آخر في مؤشر الترابط الحالي ، ينتقل مؤشر الترابط الحالي إلى حالة حظر حتى يتم تشغيل العملية الأخرى ، وينتقل الخيط الحالي من الحظر إلى الحالة الجاهزة.
6. إيقاظ مؤشر الترابط: تستيقظ طريقة الإخطار () في فئة الكائن على مؤشر ترابط واحد في انتظار شاشة الكائن هذا. إذا كانت جميع المواضيع تنتظر على هذا الكائن ، فسيتم تحديد أحد مؤشرات الترابط. الاختيار تعسفي ويحدث عند اتخاذ قرار بشأن التنفيذ. ينتظر الخيط على شاشة الكائن عن طريق استدعاء إحدى طرق الانتظار. لا يمكن تنفيذ مؤشر ترابط الاستيقاظ حتى يتخلى مؤشر الترابط الحالي عن القفل على هذا الكائن. سوف يتنافس مؤشر ترابط الاستيقاظ مع جميع مؤشرات الترابط الأخرى التي تتم مزامنتها بنشاط على الكائن بطريقة تقليدية ؛ على سبيل المثال ، لا يحتوي مؤشر ترابط Waken على امتيازات أو عيوب موثوقة في كونها الخيط التالي الذي يحبس هذا الكائن. تحتوي طريقة مماثلة أيضًا على إخطار () يستيقظ جميع سلاسل الرسائل التي تنتظر شاشة الكائن هذه.
ملاحظة: تم إلغاء طريقتان تعليق () واستئناف () في الخيط في JDK1.5 ولن يتم تقديمه مرة أخرى. لأن هناك ميل إلى طريق مسدود.
6. وصف الوظائف المشتركة
① Sleep (Millis Long): دعنا ننام الخيط الذي ينفذ حاليًا ضمن العدد المحدد من المللي ثانية (تعليق التنفيذ)
② join (): يشير إلى الانتظار لإنهاء مؤشر الترابط t.
كيفية استخدامه.
الانضمام هو طريقة فئة الموضوع. يسمى مباشرة بعد بدء الخيط. وهذا هو ، وظيفة Join () هي: "انتظر لإنهاء الخيط". ما يجب فهمه هنا هو أن الخيط يشير إلى الموضوع الرئيسي الذي ينتظر إنهاء موضوع الطفل. بمعنى أن الكود بعد مؤشر ترابط الطفل يستدعي طريقة Join () ، ولا يمكن تنفيذه إلا حتى يتم الانتهاء من خيط الطفل.
الموضوع T = جديد Athread () ؛ T.Start () ؛ t.join () ؛
لماذا تستخدم طريقة Join ()
في كثير من الحالات ، يولد الخيط الرئيسي ويبدأ خيط الطفل. إذا كانت هناك حاجة إلى عدد كبير من العمليات المستهلكة للوقت في موضوع الطفل ، فغالبًا ما ينتهي الخيط الرئيسي قبل خيط الطفل. ومع ذلك ، إذا كان الخيط الرئيسي يحتاج إلى استخدام نتيجة معالجة مؤشر ترابط الطفل بعد معالجة المعاملات الأخرى ، أي أن الخيط الرئيسي يحتاج إلى انتظار مؤشر ترابط الطفل لإكمال التنفيذ قبل الانتهاء. في هذا الوقت ، يجب استخدام طريقة Join ().
لا انضمام. /** *@Functon MultiThReading Learning ، Join *@efult Lin Bingwen *@time 2015.3.9 */package com.multithread.join ؛ class thread1 يمتد thread {private string name ؛ public thread1 (اسم السلسلة) {super (name) ؛ this.name = name ؛ } public void run () {system.out.println (thread.currentThRead (). getName () + "thread starts!") ؛ لـ (int i = 0 ؛ i <5 ؛ i ++) {system.out.println ("subthread"+name+"run:"+i) ؛ جرب {Sleep ((int) Math.Random () * 10) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }} system.out.println (thread.currentThRead (). getName () + "انتهاء تشغيل الموضوع!") ؛ }} الفئة العامة الرئيسية {public static void main (string [] args) {system.out.println (thread.currentthread (). getName ()+"Main Thread Run Start!") ؛ thread1 mth1 = new thread1 ("a") ؛ thread1 mth2 = new thread1 ("b") ؛ mth1.start () ؛ mth2.start () ؛ System.out.println (thread.currentThRead (). getName ()+ "نهايات تشغيل الخيط الرئيسي!") ؛ }} نتيجة الإخراج:
يبدأ الموضوع الرئيسي الرئيسي!
ينتهي الموضوع الرئيسي الرئيسي!
ب بدء تشغيل الموضوع!
موضوع الطفل ب يدير: 0
يبدأ تشغيل الموضوع!
خيط الطفل A يدير: 0
الخيط الطفل ب يدير: 1
خيط الطفل A يدير: 1
خيط الطفل A يدير: 2
خيط الطفل A يدير: 3
خيط الطفل A يدير: 4
ينتهي تشغيل الموضوع!
موضوع الطفل ب يدير: 2
موضوع الطفل ب يدير: 3
موضوع الطفل ب يدير: 4
ب يركض الموضوع!
وجدت أن الخيط الرئيسي انتهى في وقت مبكر من خيط الطفل
ينضم
الفئة العامة الرئيسية {public static void main (string [] args) {system.out.println (thread.currentThRead (). thread1 mth1 = new thread1 ("a") ؛ thread1 mth2 = new thread1 ("b") ؛ mth1.start () ؛ mth2.start () ؛ حاول {mth1.join () ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } حاول {mth2.join () ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } system.out.println (thread.currentThRead (). getName ()+ "نهايات تشغيل الموضوع الرئيسية!") ؛ }} نتائج التشغيل:
يبدأ الموضوع الرئيسي الرئيسي!
يبدأ تشغيل الموضوع!
خيط الطفل A يدير: 0
ب بدء تشغيل الموضوع!
موضوع الطفل ب يدير: 0
خيط الطفل A يدير: 1
الخيط الطفل ب يدير: 1
خيط الطفل A يدير: 2
موضوع الطفل ب يدير: 2
خيط الطفل A يدير: 3
موضوع الطفل ب يدير: 3
خيط الطفل A يدير: 4
موضوع الطفل ب يدير: 4
ينتهي تشغيل الموضوع!
سوف ينتظر الخيط الرئيسي بالتأكيد حتى يتم الانتهاء من خيوط الطفل قبل أن ينتهي.
③yield (): يتوقف مؤقتًا عن كائن مؤشر الترابط الذي ينفذ حاليًا وينفذ مؤشرات الترابط الأخرى.
وظيفة طريقة thread.yield () هي: إيقاف كائن مؤشر الترابط الذي ينفذ حاليًا وتنفيذ مؤشرات الترابط الأخرى.
ما يجب أن يفعله العائد () هو استعادة مؤشر ترابط التشغيل الحالي إلى الحالة القابلة للتشغيل للسماح لخيوط أخرى بنفس الأولوية للحصول على فرصة تشغيل. لذلك ، فإن الغرض من استخدام العائد () هو السماح لخيوط نفس الأولوية بالأداء بشكل مناسب. ومع ذلك ، في الواقع ، لا يمكن ضمان العائد () لتحقيق الغرض من الامتياز ، لأنه قد يتم اختيار مؤشر ترابط الامتياز مرة أخرى من قبل جدولة مؤشرات الترابط.
الخلاصة: العائد () لا يتسبب في أن يذهب الخيط إلى حالة الانتظار/النوم/الحظر. في معظم الحالات ، سيؤدي العائد () إلى انتقال الخيط من تشغيل الحالة ، ولكن قد لا يعمل. يمكنك رؤية الصورة أعلاه.
/** *@functon multiThReading Learning Liversing *@efult lin bingwen *@time 2015.3.9 */package com.multithRead.yield ؛ class threadyield تمديد Thread {public threadield (اسم السلسلة) {super (name) ؛ } Override public void run () {for (int i = 1 ؛ i <= 50 ؛ i ++) {system.out.println ("" + this.getName () + "-----" + i) ؛ // عندما يكون عمري 30 عامًا ، سيتخلى مؤشر الترابط عن وقت وحدة المعالجة المركزية ويسمح لخيوط أخرى أو مؤشرات الترابط الخاصة به (أي كل من يمسكه أولاً بتنفيذه) إذا (i == 30) {this.yield () ؛ }}}} الفئة العامة الرئيسية {public static void main (string [] args) {threadyield yt1 = new threadyield ("Zhang San") ؛ threadyield yt2 = جديد threadyield ("li si") ؛ yt1.start () ؛ yt2.start () ؛ }} نتائج التشغيل:
الحالة الأولى: سيحصل Li Si (Thread) على وقت وحدة المعالجة المركزية عند تنفيذها حتى 30. في هذا الوقت ، يمسك Zhang San (Thread) بوقت وحدة المعالجة المركزية وينفذها.
الموقف الثاني: عندما يتم تنفيذ Li Si (Thread) إلى 30 ، سيتم التخلي عن وقت وحدة المعالجة المركزية. في هذا الوقت ، يمسك Li Si (Thread) بوقت وحدة المعالجة المركزية وينفذها.
الفرق بين النوم () والعائد ()
الفرق بين النوم () والعائد ()): النوم () يؤدي إلى دخول الخيط الحالي إلى حالة راكدة ، وبالتالي لن يتم تنفيذ مؤشر الترابط الذي ينفذ النوم () في غضون الوقت المحدد ؛ يؤدي العائد () فقط إلى عودة مؤشر الترابط الحالي إلى الحالة القابلة للتنفيذ ، بحيث يمكن تنفيذ عائد تنفيذ مؤشر الترابط () مباشرة بعد إدخال الحالة القابلة للتنفيذ.
تتسبب طريقة النوم في نوم الخيط الذي يعمل حاليًا لفترة زمنية ويدخل إلى حالة غير قابلة للعبادة. يتم تعيين طول هذه الفترة بواسطة البرنامج. تتيح طريقة العائد للموضوع الحالي التخلي عن ملكية وحدة المعالجة المركزية ، لكن وقت النقل أمر غير قابل للاسترداد. في الواقع ، تتوافق طريقة العائد () مع العملية التالية: تحقق أولاً مما إذا كانت هناك مؤشرات ترابط ذات نفس الأولوية حاليًا في نفس الحالة القابلة للتشغيل. إذا كان الأمر كذلك ، فاستمر في تشغيل ملكية وحدة المعالجة المركزية لهذا الموضوع ، وإلا ، في تشغيل الخيط الأصلي. لذا فإن طريقة العائد () تسمى "الامتياز" ، والتي تمنح الفرصة للتشغيل لخيوط أخرى بنفس الأولوية
بالإضافة إلى ذلك ، تتيح طريقة النوم مؤشرات الترابط ذات الأولوية المنخفضة الحصول على فرص تشغيل ، ولكن عند تنفيذ طريقة العائد () ، لا يزال الخيط الحالي في حالة قابلة للتشغيل ، لذلك من المستحيل التخلي عن مؤشرات الترابط ذات الأولوية الأدنى للحصول على ملكية وحدة المعالجة المركزية لاحقًا. في نظام التشغيل ، إذا لم يستدعي مؤشر ترابط الأولوية الأعلى طريقة النوم ولم يتم حظره بواسطة I/O ، فيمكن أن ينتظر مؤشر ترابط الأولوية الأدنى فقط جميع مؤشرات الترابط ذات الأولوية الأعلى لتتوفر لها فرصة لتشغيلها.
④setPriority (): تغيير أولوية الخيط.
min_priority = 1
norm_priority = 5
max_priority = 10
الاستخدام:
Thread4 T1 = New Thread4 ("T1") ؛
Thread4 T2 = New Thread4 ("T2") ؛
t1.setPriority (thread.max_priority) ؛
t2.setPriority (thread.min_priority) ؛
⑤Interrupt (): مقاطعة خيط. طريقة النهاية هذه تقريبية إلى حد ما. إذا فتح مؤشر ترابط T مورد ولم يكن لديه وقت لإغلاقه ، فهذا هو ، يتم إجبار طريقة التشغيل على إنهاء الخيط قبل تنفيذها ، مما سيؤدي إلى إغلاق المورد.
أفضل طريقة لإنهاء العملية هي استخدام برنامج المثال لوظيفة Sleep (). يتم استخدام متغير منطقي في فئة مؤشرات الترابط للتحكم عند انتهاء طريقة Run (). بمجرد انتهاء طريقة التشغيل () ، ينتهي الخيط.
⑥wait ()
يجب استخدام OBJ.WAIT () و OBJ.NOTIFY () مع متزامن (OBJ) ، أي الانتظار والإخطار على قفل OBJ الذي تم الحصول عليه. من وجهة نظر متزامنة ، يجب أن تكون OBJ.WAIT () ، و OBJ.NOTIFY في كتلة البيان المتزامنة (OBJ) {...}. من منظور وظيفي ، يعني الانتظار أنه بعد أن يكتسب مؤشر الترابط قفل الكائن ، يطلق بنشاط قفل الكائن ، وينام مؤشر الترابط. لا يمكن الحصول على قفل الكائن وسيستمر التنفيذ حتى يقوم مؤشر ترابط آخر بإخطار الكائن () بإيقاظ مؤشر الترابط. الإخطار المقابل () هو عملية إيقاظ قفل الكائن. ولكن هناك شيء واحد يجب ملاحظته هو أنه بعد مكالمة الإخطار () ، لم يتم إصدار قفل الكائن على الفور ، ولكن يتم تنفيذ كتلة العبارة المتزامنة () {} المقابلة ، ويتم إصدار القفل تلقائيًا ، وسيقوم JVM باختيار مؤشر ترابط بشكل عشوائي من مؤشر ترابط كائن WAIT () ، وتعيينه إلى قفل الكائن ، ويستيقظ في مؤشر ترابط ، ومتابعة التنفيذ. وهذا يوفر عمليات التزامن والاستيقاظ بين المواضيع. يمكن لكل من Thread.sleep () و Object.wait () إيقاف مؤشر الترابط الحالي والتحكم في وحدة المعالجة المركزية. الفرق الرئيسي هو أنه على الرغم من أن object.wait () تطلق وحدة المعالجة المركزية ، فإنها تطلق التحكم في قفل الكائن.
لا يكفي أن نفهم من الناحية المفاهيمية ، ويجب اختبارها في أمثلة عملية لفهمها بشكل أفضل. يجب أن يكون المثال الأكثر كلاسيكيًا لتطبيق Object.wait () و Object.Notify () مشكلة طباعة ABC مع ثلاثة مؤشرات ترابط. هذا سؤال مقابلة كلاسيكي نسبيًا ، والأسئلة كما يلي:
إنشاء ثلاثة مؤشرات ترابط ، مؤشر ترابط A يطبع 10 مرات ، الخيط B المطبوعات B 10 مرات ، المطبوعات الخيط C C يمكن حل هذه المشكلة بسهولة عن طريق استخدام كائن WAIT () وإخطار (). الرمز كما يلي:
/** * انتظر الاستخدام * Author Dreamsea * @ttime 2015.3.9 */package com.multithread.wait ؛ public class myThReadPrinter2 تنفذ runnable {private string name ؛ كائن خاص سابق ؛ كائن خاص الذات ؛ MyThReadPrinter2 الخاص (اسم السلسلة ، الكائن السابق ، الكائن الذاتي) {this.name = name ؛ this.prev = prev ؛ هذا. } Override public void run () {int count = 10 ؛ بينما (count> 0) {synchronized (prev) {synchronized (self) {system.out.print (name) ؛ عدد--؛ Self.Notify () ؛ } حاول {prev.wait () ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ }}}} public static void main (string [] args) يرمي الاستثناء {object a = new Object () ؛ الكائن B = كائن جديد () ؛ كائن C = كائن جديد () ؛ MyThReadPrinter2 pa = new MyThReadPrinter2 ("A" ، C ، A) ؛ MyThReadPrinter2 pb = new MyThReadPrinter2 ("B" ، a ، b) ؛ MyThReadPrinter2 pc = new MyThReadPrinter2 ("C" ، B ، C) ؛ موضوع جديد (PA) .start () ؛ thread.sleep (100) ؛ // تأكد من تنفيذ مؤشر ترابط جديد (PB) .start () ؛ thread.sleep (100) ؛ }} نتيجة الإخراج:
abcabcabcabcabcabcabcabcabcabcabcabc
دعنا أولاً نشرح فكرتها العامة. من منظور عام ، هذه المشكلة هي عملية إيقاظ متزامنة بين ثلاثة مؤشرات ترابط. الغرض الرئيسي هو تنفيذ ثلاثة مؤشرات ترابط في Threada-> threadb-> threadc-> حلقة Threadc. من أجل التحكم في ترتيب تنفيذ مؤشرات الترابط ، يجب تحديد ترتيب الاستيقاظ والانتظار ، لذلك يجب أن يحتفظ كل مؤشر ترابط بقفل كائن في نفس الوقت قبل أن يتمكن من الاستمرار في التنفيذ. قفل الكائن هو السابق ، وهو قفل الكائن الذي يحتفظ به الخيط السابق. آخر هو قفل الكائن. الفكرة الرئيسية هي أنه من أجل التحكم في ترتيب التنفيذ ، يجب عليك أولاً الاحتفاظ بقفل Prev ، أي ، يجب أن يصدر مؤشر الترابط السابق قفل الكائن الخاص به ، ثم التقدم بطلب للحصول على قفل الكائن الخاص به. طباعة عندما كلاهما كلاهما. بعد ذلك ، اتصل أولاً بـ Self.Notify () لإطلاق قفل الكائن الخاص به ، واستيقظ على موضوع الانتظار التالي ، ثم اتصل بـ Prev.wait () لإطلاق قفل الكائن السابق ، وإنهاء مؤشر الترابط الحالي ، وانتظر حتى يتم إيقاظ الحلقة مرة أخرى. قم بتشغيل الرمز أعلاه ويمكنك العثور على ثلاثة مؤشرات ترابط طباعة ABC في حلقة ، ما مجموعه 10 مرات. العملية الرئيسية لتشغيل البرنامج هي أن الخيط A هو أول من يتم تشغيله ، ويحمل أقفال كائن C و A ، ثم يطلق أقفال A و C ، ويستيقظ B. تفكر في الأمر بعناية ، ستجد أن هناك مشكلة ، وهي الحالة الأولية. يتم تشغيل المواضيع الثلاثة بترتيب A و B و C. وفقًا للأفكار السابقة ، يستيقظ A B ، B ، يستيقظ C ، C ، ثم يستيقظ A. ومع ذلك ، يعتمد هذا الافتراض على ترتيب جدولة مؤشرات الترابط والتنفيذ في JVM.
الفرق بين الانتظار والنوم
النقاط المشتركة:
1. إنهم جميعًا في بيئة متعددة الخيوط ، ويمكنهم منع العدد المحدد من المللي ثانية في مكالمة البرنامج والعودة.
2. كل من Wait () و Sleep () يمكن أن يقطعوا حالة توقف الخيط من خلال طريقة المقاطعة () ، بحيث يلقي مؤشر الترابط على الفور مقاطعًا.
إذا أراد مؤشر الترابط A إنهاء مؤشر الترابط B على الفور ، فيمكن استدعاء طريقة المقاطعة على مثيل مؤشر الترابط المقابل للموضوع B. إذا كان الموضوع B ينتظر/نوم/انضمام في هذه اللحظة ، فسوف يرمي مؤشر الترابط B على الفور مقاطعًا ، وإعادته مباشرةً في Catch () {} لإنهاء مؤشر الترابط بأمان.
تجدر الإشارة إلى أن المقاطعة يتم إلقاؤها بواسطة الخيط نفسه من الداخل ، وليس بواسطة طريقة المقاطعة (). عندما يتم استدعاء Interrupt () على مؤشر ترابط ، إذا كان مؤشر الترابط ينفذ الكود العادي ، فلن يلقي مؤشر الترابط مقاصة على الإطلاق. ومع ذلك ، بمجرد أن يدخل الخيط WAIT ()/Sleep ()/join () ، سيتم إلقاء مقاطع المقاطع على الفور.
الاختلافات:
1. طرق فئة الخيط: Sleep () ، العائد () ، إلخ.
أساليب الكائن: انتظر () وإخطار () ، إلخ.
2. كل كائن لديه قفل للتحكم في الوصول المتزامن. يمكن أن تتفاعل الكلمة الرئيسية المتزامنة مع قفل الكائن لتحقيق مزامنة مؤشر الترابط.
لا تطلق طريقة النوم القفل ، بينما تطلق طريقة الانتظار القفل ، بحيث يمكن لخيوط أخرى استخدام كتل أو طرق تحكم متزامنة.
3. الانتظار والإخطار والإخطار لا يمكن استخدامه إلا في طرق التحكم في المزامنة أو كتل التحكم في المزامنة ، بينما يمكن استخدام النوم في أي مكان.
4. يجب أن ينطلق النوم على استثناءات ، في حين أن الانتظار والإخطار والإخطار لا يحتاج إلى استثناءات ، وبالتالي فإن الفرق الأكبر بين أساليب النوم () والانتظار () هو:
عندما ينام النوم () ، احتفظ بقفل الكائن ولا يزال يمتلك القفل ؛
عندما تنام () ينام ، يتم إطلاق قفل الكائن.
ومع ذلك ، يمكن أن يقطعت WAIT () و Sleep () حالة توقف الخيط من خلال طريقة المقاطعة () ، بحيث يرمي مؤشر الترابط على الفور مقاطعًا (ولكن لا ينصح باستخدام هذه الطريقة).
طريقة النوم ()
يتسبب Sleep () في دخول الخيط الحالي إلى حالة راكدة (يمنع الخيط الحالي) ، والتخلي عن استخدام الكأس ، والغرض منه هو منع مؤشر الترابط الحالي من احتلال موارد وحدة المعالجة المركزية التي حصلت عليها العملية وحدها ، وذلك لترك وقت معين لتنفيذ مؤشرات الترابط الأخرى ؛
النوم () هو طريقة ثابتة لفئة الخيط ؛ لذلك لا يمكن تغيير قفل جهاز الكائن ، لذلك عند استدعاء طريقة Sleep () في كتلة متزامنة ، على الرغم من أن الخيط نائم ، لا يتم إطلاق قفل جهاز الكائن ، ولا يمكن لخيوط أخرى الوصول إلى الكائن (على الرغم من أنه لا يزال يحمل قفل الكائن حتى عندما يكون نائمًا).
بعد انتهاء وقت النوم () النوم () ، لا يتم تنفيذ الخيط بالضرورة على الفور ، لأنه قد يتم تشغيل مؤشرات الترابط الأخرى ولا يتم جدولة للتخلي عن التنفيذ ما لم يكن للموضوع أولوية أعلى.
انتظر () الطريقة
طريقة الانتظار () هي طريقة في فئة الكائن ؛ عندما يقوم مؤشر ترابط بتنفيذ طريقة WAIT () ، فإنه يدخل تجمع انتظار مرتبط بالكائن ، وفي الوقت نفسه يفقد (الإصدارات) قفل جهاز الكائن (يفقد قفل الماكينة مؤقتًا ، وسيحتاج وقت الانتظار (المهلة الطويلة) أيضًا إلى إرجاع قفل الكائن) ؛ الخيوط الأخرى يمكنها الوصول إليها ؛
انتظر () يستخدم الإخطار أو الإخطار أو وقت النوم المحدد لإيقاظ الخيط في تجمع الانتظار الحالي.
يجب وضع WIAT () في الكتلة المتزامنة ، وإلا سيتم طرح استثناء "java.lang.illegalmonitorstateException" عند تشغيل البرنامج.
7. شرح مصطلحات الخيط المشترك
الموضوع الرئيسي: الخيط الذي تم إنشاؤه بواسطة برنامج Call JVM Main ().
الموضوع الحالي: هذا مفهوم مربك. يشير بشكل عام إلى عملية تم الحصول عليها من خلال thread.currentThread ().
موضوع الخلفية: يشير إلى مؤشر ترابط يوفر الخدمات لخيوط أخرى ، والمعروفة أيضًا باسم خيط الخفي. موضوع مجموعة GROBAGE JVM هو موضوع خلفية. الفرق بين مؤشر ترابط المستخدم وخيط Daemon هو ما إذا كان يجب انتظار مؤشر الترابط الرئيسي لإنهاء مؤشر ترابط المقدمة اعتمادًا على نهاية الخيط الرئيسي: يشير إلى مؤشر الترابط الذي يقبل خدمة مؤشر ترابط الخلفية. في الواقع ، ترتبط مؤشرات ترابط الخلفية الأمامية معًا ، تمامًا مثل العلاقة بين الدمى والمعالج وراء الكواليس. الدمى هو الخيط الأمامي ، والمعالج وراء الكواليس هو مؤشر ترابط الخلفية. المواضيع التي تم إنشاؤها بواسطة مؤشرات الترابط المقدمة هي أيضًا مؤشرات ترابط المقدمة افتراضيًا. يمكنك استخدام طرق ISDAEMON () و setdaemon () لتحديد وتعيين ما إذا كان مؤشر ترابط هو موضوع خلفية.
بعض الطرق الشائعة لفئات الخيوط:
النوم (): إجبار خيط على النوم في N Milliseconds.
isalive (): يحدد ما إذا كان الخيط يبقى.
انضم (): انتظر لإنهاء الموضوع.
ActiveCount (): عدد المواضيع النشطة في البرنامج.
تعداد (): تعداد المواضيع في البرنامج.
CurrentThread (): يحصل على الموضوع الحالي.
ISDAEMON (): ما إذا كان الخيط هو خيط خفي.
setDaemon (): اضبط مؤشر ترابط كخيط خفي. (الفرق بين مؤشر ترابط المستخدم وخيط الخفي هو ما إذا كان سينتظر حتى ينتهي الخيط الرئيسي اعتمادًا على نهاية الخيط الرئيسي)
setName (): اضبط اسمًا للمعلومات.
انتظر (): إجبار موضوع على الانتظار.
إخطار (): إخطار موضوع لمواصلة التشغيل.
setPriority (): يحدد أولوية الخيط.
8. تزامن الموضوع
1. هناك نطاقان من الكلمات الرئيسية المتزامنة:
1) هو داخل مثيل كائن. يمكن أن تمنع Amethod () {} متزامنة متزامنة من الوصول إلى الطريقة المتزامنة لهذا الكائن في نفس الوقت (إذا كان للكائن طرقًا متعددة متزامنة ، طالما أن أحد مؤشرات ترابط يصل إلى أحد الطرق المتزامنة ، لا يمكن لخيوط أخرى الوصول إلى أي طرق متزامنة في الكائن في نفس الوقت). في هذا الوقت ، لا تنقطع الطريقة المتزامنة لحالات كائن مختلفة. That is to say, other threads can still access the synchronized method in another object instance of the same class at the same time;
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象;
3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;
Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了synchronized关键字就可以轻松地解决多线程共享数据同步问题。到底如何?还得对synchronized关键字的作用进行深入了解才可定论。
总的说来,synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
在进一步阐述之前,我们需要明确几点:
A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁而且同步方法很可能还会被其他线程的对象访问。
B.每个对象只有一个锁(lock)与之相关联。
C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
接着来讨论synchronized用到不同地方对代码产生的影响:
假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都可以调用它们。
1. 把synchronized当作函数修饰符时,示例代码如下:
Public synchronized void methodAAA(){//….}这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。
上边的示例代码等同于如下代码:
public void methodAAA(){synchronized (this) // (1){ //…..}}(1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱:(
2.同步块,示例代码如下:
public void method3(SomeObject so) { synchronized(so){ //…..}}这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:
class Foo implements Runnable{ private byte[] lock = new byte[0]; // 特殊的instance变量Public void methodA(){ synchronized(lock) { //… }}//…..}注:零长度的byte数组对象创建起来将比任何对象都经济查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
3.将synchronized作用于static 函数,示例代码如下:
Class Foo{public synchronized static void methodAAA() // 同步的static 函数{//….}public void methodBBB(){ synchronized(Foo.class) // class literal(类名称字面常量)} }代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
记得在《Effective Java》一书中看到过将Foo.class和P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。
可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
九、线程数据传递
在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果。但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别。由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。
9.1、通过构造方法传递数据在创建线程时,必须要建立一个Thread类的或其子类的实例。因此,我们不难想到在调用start方法之前通过线程类的构造方法将数据传入线程。并将传入的数据使用类变量保存起来,以便线程使用(其实就是在run方法中使用)。下面的代码演示了如何通过构造方法来传递数据:
package mythread; public class MyThread1 extends Thread { private String name; public MyThread1(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { Thread thread = new MyThread1("world"); thread.start () ؛ }}由于这种方法是在创建线程对象的同时传递数据的,因此,在线程运行之前这些数据就就已经到位了,这样就不会造成数据在线程运行后才传入的现象。如果要传递更复杂的数据,可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全,但如果要传递的数据比较多时,就会造成很多不便。由于Java没有默认参数,要想实现类似默认参数的效果,就得使用重载,这样不但使构造方法本身过于复杂,又会使构造方法在数量上大增。因此,要想避免这种情况,就得通过类方法或类变量来传递数据。
9.2、通过变量和方法传递数据
向对象中传入数据一般有两次机会,第一次机会是在建立对象时通过构造方法将数据传入,另外一次机会就是在类中定义一系列的public的方法或变量(也可称之为字段)。然后在建立完对象后,通过对象实例逐个赋值。下面的代码是对MyThread1类的改版,使用了一个setName方法来设置name变量:
package mythread; public class MyThread2 implements Runnable { private String name; public void setName(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { MyThread2 myThread = new MyThread2(); myThread.setName("world"); Thread thread = new Thread(myThread); thread.start () ؛ }} 9.3、通过回调函数传递数据
上面讨论的两种向线程中传递数据的方法是最常用的。但这两种方法都是main方法中主动将数据传入线程类的。这对于线程来说,是被动接收这些数据的。然而,在有些应用中需要在线程运行的过程中动态地获取数据,如在下面代码的run方法中产生了3个随机数,然后通过Work类的process方法求这三个随机数的和,并通过Data类的value将结果返回。从这个例子可以看出,在返回value之前,必须要得到三个随机数。也就是说,这个value是无法事先就传入线程类的。
package mythread; class Data { public int value = 0; } class Work { public void process(Data data, Integer numbers) { for (int n : numbers) { data.value += n; } } } public class MyThread3 extends Thread { private Work work; public MyThread3(Work work) { this.work = work; } public void run() { java.util.Random random = new java.util.Random(); Data data = new Data(); int n1 = random.nextInt(1000); int n2 = random.nextInt(2000); int n3 = random.nextInt(3000); work.process(data, n1, n2, n3); // Use the callback function System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+" + String.valueOf(n3) + "=" + data.value); } public static void main(String[] args) { Thread thread = new MyThread3(new Work()); thread.start () ؛ }}The above is a detailed explanation of Java multi-threading. I hope it can help you learn this part of the knowledge. شكرا لدعمكم لهذا الموقع!