Java Multithreading (واحد)
كنقطة معرفة مهمة للغاية في Java ، لا يزال من الضروري تلخيصها هنا.
1. دورة حياة الخيط والحالات الأساسية الخمس
فيما يتعلق بدورة حياة الخيوط في جافا ، دعونا نلقي نظرة أولاً على الصورة الكلاسيكية التالية:
يغطي الرقم أعلاه بشكل أساسي نقاط المعرفة المهمة للرواية المتعددة في Java. بمجرد إتقان نقاط المعرفة في الرقم أعلاه ، ستقوم بشكل أساسي بإتقان الروابط المتعددة في Java. بما في ذلك:
خيوط Java لها خمس حالات أساسية
حالة جديدة (جديدة): عند إنشاء زوج كائن مؤشر ترابط ، فإنه يدخل حالة جديدة ، مثل: thread t = new MyThread () ؛
حالة جاهزة (Runnable): عندما تكون طريقة Start () لكائن مؤشر الترابط (T.Start () ؛) ، يدخل مؤشر الترابط إلى الحالة الجاهزة. مؤشر ترابط في الحالة الجاهزة يعني فقط أن الخيط جاهز وينتظر وحدة المعالجة المركزية لجدولة التنفيذ في أي وقت ، وليس أن مؤشر الترابط سيتم تنفيذه مباشرة بعد تنفيذ T.Start () ؛
حالة التشغيل: عندما تبدأ وحدة المعالجة المركزية في جدولة مؤشرات الترابط في الحالة الجاهزة ، يمكن تنفيذ مؤشر الترابط حقًا ، أي أنه يدخل الحالة الجارية. ملاحظة: الحالة الجاهزة هي الإدخال الوحيد لحالة الجري ، أي إذا أراد مؤشر ترابط الدخول إلى حالة التشغيل للتنفيذ ، فيجب أن يكون أولاً في الحالة الجاهزة ؛
الحالة المحظورة: لسبب ما ، يتخلى مؤشر ترابط في حالة الجري مؤقتًا عن استخدامه لوحدة المعالجة المركزية ويوقف التنفيذ. في هذا الوقت ، يدخل حالة الحجب. لن تتاح لها الفرصة لاستدعاء وحدة المعالجة المركزية مرة أخرى لدخول حالة الجري. وفقًا لأسباب الحجب ، يمكن تقسيم حالات الحظر إلى ثلاثة أنواع:
1. في انتظار الحظر: يقوم مؤشر الترابط في حالة التشغيل بتنفيذ طريقة WAIT () لجعل مؤشر الترابط يدخل في انتظار حالة الحظر ؛
2. الحظر المتزامن- فشل مؤشر الترابط في الحصول على قفل المزامنة المتزامن (لأن القفل يشغله مؤشرات ترابط أخرى) ، وسيدخل حالة الحظر المتزامنة ؛
3. الحظر الآخر-سيدخل الخيط حالة حظر عن طريق الاتصال بـ Sleep () أو الانضمام () من الموضوع أو إصدار طلب I/O. عندما تم توقيت Sleep () ، انتظرت الانضمام () لإنهاء الخيط أو توقيته ، أو تم الانتهاء من معالجة الإدخال/الإخراج ، حيث تم إعادة إدخال الخيط إلى الحالة الجاهزة.
Dead: انتهى الخيط من تنفيذ أو خروج طريقة Run () بسبب استثناء ، وينهي الخيط دورة حياته.
2. خلق وبدء تشغيل Java Multithreads
هناك ثلاثة أشكال أساسية لإنشاء الخيط في جافا
1. ورث فئة الخيط وتجاوز طريقة Run () للفئة.
فئة myThread يمتد الموضوع {private int i = 0 ؛ Override public void run () {for (i = 0 ؛ i <100 ؛ i ++) {system.out.println (thread.currentThread (). getName () + "" + i) ؛ }}} Througtest thouldtest {public static void main (string [] args) {for (int i = 0 ؛ i <100 ؛ i ++) {system.out.println (thread.currentThread (). getName () + "" + i) ؛ if (i == 30) {thread myThRead1 = new MyThread () ؛ // إنشاء مؤشر ترابط جديد myThread1 يدخل هذا الموضوع إلى موضوع الحالة الجديد myThread2 = جديد myThread () ؛ // إنشاء موضوع جديد myThread2 يدخل هذا الموضوع إلى الحالة الجديدة mythread1.start () ؛ // استدعاء طريقة START () لجعل مؤشر الترابط يدخل الحالة الجاهزة myThRead2.Start () ؛ // استدعاء طريقة START () لجعل مؤشر الترابط أدخل الحالة الجاهزة}}}}}كما هو موضح أعلاه ، وراثي فئة مؤشرات الترابط ، يتم تعريف myThread فئة مؤشرات الترابط الجديدة عن طريق الكتابة فوق طريقة Run () ، حيث يمثل هيئة طريقة Run () المهمة التي يحتاجها مؤشر الترابط إلى إكمالها ، ويسمى هيئة تنفيذ مؤشر الترابط. عند إنشاء كائن فئة مؤشرات الترابط هذا ، يتم إنشاء مؤشر ترابط جديد وإدخال حالة مؤشر الترابط الجديدة. من خلال استدعاء طريقة START () المشار إليها بواسطة كائن مؤشر الترابط ، يدخل مؤشر الترابط إلى الحالة الجاهزة. في هذا الوقت ، لا يجوز تنفيذ الخيط على الفور ، اعتمادًا على توقيت جدولة وحدة المعالجة المركزية.
2. قم بتنفيذ الواجهة القابلة للتشغيل وتجاوز طريقة التشغيل () للواجهة. طريقة Run () هي أيضًا هيئة تنفيذ مؤشرات الترابط ، وإنشاء مثيل لفئة التنفيذ القابلة للتشغيل ، واستخدم هذا المثيل كهدف لفئة مؤشرات الترابط لإنشاء كائن مؤشر ترابط. كائن الخيط هو كائن مؤشر الترابط الحقيقي.
class myrunnable الأدوات runnable {private int i = 0 ؛ Override public void run () {for (i = 0 ؛ i <100 ؛ i ++) {system.out.println (thread.currentThread (). getName () + "" + i) ؛ }}} Througtest thouldtest {public static void main (string [] args) {for (int i = 0 ؛ i <100 ؛ i ++) {system.out.println (thread.currentThread (). getName () + "" + i) ؛ if (i == 30) {runnable myrunnable = new myrunnable () ؛ // قم بإنشاء كائن من Thread1 = موضوع جديد (MyRunable) ؛ // قم بإنشاء مؤشر ترابط جديد مع MyRunnable كمعلومات Thread Thread2 = مؤشر ترابط جديد (MyRunnable) ؛ thread1.start () ؛ // استدعاء طريقة START () لجعل مؤشر الترابط أدخل thread2.start () ؛ }}}} أعتقد أن الجميع على دراية بالطريقتين أعلاه لإنشاء مؤشرات ترابط جديدة. إذن ما هي العلاقة بين الخيط والركض؟ دعونا نلقي نظرة أولاً على المثال التالي.
Througtest thouldtest {public static void main (string [] args) {for (int i = 0 ؛ i <100 ؛ i ++) {system.out.println (thread.currentThread (). getName () + "" + i) ؛ if (i == 30) {runnable myrunnable = new myrunnable () ؛ موضوع الموضوع = جديد myThread (myrunnable) ؛ thread.start () ؛ }}}} class myrunnable reglements {private int i = 0 ؛ Override public void run () {system.out.println ("in myrunnable run") ؛ لـ (i = 0 ؛ i <100 ؛ i ++) {system.out.println (thread.currentThRead (). getName () + "" + i) ؛ }}} class myThread يمتد Thread {private int i = 0 ؛ MyThereD (RunNable Runnable) {super (runnable) ؛ } Override public void run () {system.out.println ("in mythread run") ؛ لـ (i = 0 ؛ i <100 ؛ i ++) {system.out.println (thread.currentThRead (). getName () + "" + i) ؛ }}}وبالمثل ، ينطبق الشيء نفسه على إنشاء مؤشرات ترابط تنفذ الواجهة القابلة للتشغيل ، والفرق هو ذلك
1 موضوع موضوع = جديد myThread (myrunnable) ؛
فهل يمكن لهذه الطريقة إنشاء موضوع جديد بنجاح؟ الجواب نعم. أما بالنسبة لجسم تنفيذ مؤشرات الترابط في هذا الوقت ، فهل طريقة Run () في الواجهة المائية أو طريقة Run () في فئة MyThread؟ من خلال الإخراج ، نعلم أن هيئة تنفيذ مؤشر الترابط هي طريقة Run () في فئة MyThread. في الواقع ، يكون السبب بسيطًا للغاية ، لأن فئة مؤشر الترابط نفسها تنفذ أيضًا الواجهة القابلة للتشغيل ، ويتم تحديد طريقة Run () لأول مرة في الواجهة القابلة للتشغيل.
الواجهة العامة Runnable {public Abstract void run () ؛ } دعونا نلقي نظرة على تنفيذ طريقة Run () في الواجهة القابلة للتشغيل في فئة الخيط:
Override public void run () {if (target! = null) {target.run () ؛ }}وهذا يعني ، عند تنفيذ طريقة Run () في فئة مؤشرات الترابط ، سيحدد أولاً ما إذا كان الهدف موجودًا. إذا كانت موجودة ، يتم تنفيذ طريقة Run () في الهدف ، أي طريقة Run () في الفئة التي تنفذ الواجهة القابلة للتشغيل والكتابة فوق طريقة التشغيل (). ومع ذلك ، في الأعمدة الواردة أعلاه ، نظرًا لوجود تعدد الأشكال ، لا يتم تنفيذ طريقة Run () في فئة مؤشرات الترابط على الإطلاق ، ولكن نوع وقت التشغيل ، أي طريقة Run () في فئة MyThread يتم تنفيذها مباشرة.
3. إنشاء مؤشرات ترابط باستخدام واجهات قابلة للاتصال والمستقبلية. على وجه التحديد ، فإنه ينشئ فئة تنفيذ لواجهة قابلة للاتصال وينفذ طريقة CLALL (). واستخدم فئة FutureTask لالتفاف كائن فئة التنفيذ القابل للتطبيق ، واستخدم كائن مستقبلي مستقبلي كهدف لكائن مؤشر الترابط لإنشاء مؤشر ترابط.
يبدو الأمر معقدًا بعض الشيء ، لكن سيكون من الواضح إذا نظرت إلى مثال مباشرة.
threadtest الفئة العامة {public static void main (string [] args) {callable <integer> myCallable = new myCallable () ؛ // إنشاء mycallable futureTask <integer> ft = new FutureTask <integer> (myCallable) ؛ // استخدم futureTask لالتفاف كائن mycallable لـ (int i = 0 ؛ i <100 ؛ i ++) {system.out.println (thread.currentThRead (). getName () + " + i) ؛ if (i == 30) {thread thread = new thread (ft) ؛ // كائن FutureTask ينشئ مؤشر ترابط جديد كهدف من مؤشر ترابط مؤشر الترابط. start () ؛ // يدخل مؤشر الترابط إلى الحالة الجاهزة}} system.out.println ("تم تنفيذ الخيط الرئيسي للحلقة ..") ؛ حاول {int sum = ft.get () ؛ // احصل على النتيجة التي يتم إرجاعها بواسطة طريقة Call () في نظام مؤشر ترابط جديد تم إنشاؤه حديثًا. } catch (interruptedException e) {E.PrintStackTrace () ؛ } catch (executionException e) {e.printStackTrace () ؛ }}} class mycallable armements callable <integer> {private int i = 0 ؛ // على عكس طريقة Run () ، تحتوي طريقة Call () على قيمة إرجاع Override Integer Integer Call () {int sum = 0 ؛ لـ (؛ i <100 ؛ i ++) {system.out.println (thread.currentThread (). getName () + "" + i) ؛ SUM += i ؛ } إرجاع مجموع ؛ }}بادئ ذي بدء ، وجدنا أنه عند تنفيذ الواجهة القابلة للاتصال ، لم تعد طريقة Run () هي طريقة Run () ، ولكن طريقة Call (). طريقة Call () هي هيئة تنفيذ مؤشرات الترابط ولها أيضًا قيمة إرجاع! عند إنشاء مؤشر ترابط جديد ، يتم لف الكائن القابل للضرب من خلال مقاس مستقبلي ويعمل أيضًا كهدف لكائن مؤشر الترابط. ثم انظر إلى تعريف فئة FutureTask:
تستقبل فئة FutureTask <V> RunNableFuture <v> {// ....} الواجهة العامة RunNableFuture <v> يمتد Runnable ، المستقبل <v> {void run () ؛ }لذلك ، وجدنا أن فئة FutureTask التي تنفذ فعليًا واجهات قابلة للتشغيل والمستقبلية ، مما يجعلها تتمتع بخصائص مزدوجة للمستقبل وقابل للتشغيل. من خلال الميزة القابلة للتشغيل ، يمكن استخدامها كهدف لكائن مؤشر الترابط ، وتسمح له الميزة المستقبلية بالحصول على قيمة الإرجاع لطريقة Call () في مؤشر الترابط الذي تم إنشاؤه حديثًا.
بعد تنفيذ هذا البرنامج ، نجد أن المبلغ = 4950 هو دائمًا الإخراج الأخير. "تم تنفيذ الخيط الرئيسي للحلقة ..." من المرجح أن يكون الإخراج في منتصف حلقة خيط الطفل. من آلية جدولة مؤشرات ترابط وحدة المعالجة المركزية ، نعلم أنه لا توجد مشكلة في توقيت الإخراج لـ "تم تنفيذ الخيط الرئيسي للحلقة ..." ، فلماذا سيتم إخراج SUM = 4950 إلى الأبد؟
والسبب هو أنه عند الحصول على طريقة () استدعاء مؤشر ترابط الطفل () من خلال طريقة ft.get () ، عندما لم يتم تنفيذ طريقة مؤشر ترابط الطفل بعد ، سيتم حظر طريقة ft.get () حتى يتم تنفيذ طريقة الاتصال () قبل الحصول على قيمة الإرجاع.
ما سبق يفسر بشكل رئيسي ثلاث طرق لإنشاء الخيط المشتركة. لبدء تشغيل المواضيع ، تسمى جميعها طريقة START () لكائن مؤشر الترابط. من المهم أن نلاحظ أنه لا يمكن استدعاء طريقة Start () مرتين على نفس كائن الخيط.
ثالثا. جاهز ، تشغيل وموت حالة Java Multithreading
يتم تحويل الحالة الجاهزة إلى حالة التشغيل: عندما يحصل هذا الموضوع على مورد المعالج ؛
يتم تحويل حالة التشغيل إلى الحالة الجاهزة: عندما يطلق هذا الموضوع بنشاط طريقة العائد () أو يفقد موارد المعالج أثناء التشغيل.
يتم تحويل حالة التشغيل إلى الحالة الميتة: عند اكتمال هيئة تنفيذ مؤشر الترابط أو حدوث استثناء.
تجدر الإشارة هنا إلى أنه عندما يتم استدعاء طريقة العائد () من مؤشر الترابط ، فإن انتقال مؤشر الترابط من الحالة الجارية إلى الحالة الجاهزة ، ولكن يتم تحديد خيط في الحالة الجاهزة لوحدة المعالجة المركزية بعشوائية معينة. لذلك ، قد يحدث أنه بعد استدعاء مؤشر الترابط طريقة العائد () ، لا تزال وحدة المعالجة المركزية تعمل على جدولة الخيط A.
نظرًا لاحتياجات العمل الفعلية ، غالبًا ما يتم مواجهة أن يتم إنهاء الخيط بفرصة محددة لجعله يدخل حالة ميتة. الطريقة الأكثر شيوعًا في الوقت الحالي هي تعيين متغير منطقي ، وعندما يتم استيفاء الشروط ، سيتم تنفيذ هيئة تنفيذ مؤشر الترابط بسرعة. يحب:
الفئة العامة twhertest {public static void main (string [] args) {myrunnable myrunnable = new myrunnable () ؛ موضوع الموضوع = موضوع جديد (myrunnable) ؛ لـ (int i = 0 ؛ i <100 ؛ i ++) {system.out.println (thread.currentThRead (). getName () + "" + i) ؛ if (i == 30) {thread.start () ؛ } if (i == 40) {myrunnable.stopthread () ؛ }}}} class myrunnable reglements {private boolean stop ؛ Override public void run () {for (int i = 0 ؛ i <100 &&! stop ؛ i ++) {system.out.println (thread.currentThread (). getName () + " + i) ؛ }} public void stopthread () {this.stop = true ؛ }}سوف نستمر في فرز المقالات ذات الصلة في المستقبل. شكرا لك على دعمك لهذا الموقع!
سلسلة من المقالات:
شرح مثيلات جافا متعددة الخيوط (ط)
شرح مفصل للحالات متعددة الخيوط Java (II)
شرح مفصل للحالات متعددة الخيوط Java (III)