Javaالكلاس Thread في جافا
- مقدمة
- الحالات التي يمر بها الـ Thread (Thread States)
- أمثلة شاملة
مقدمة
يستخدم الكلاس Thread لجعل البرنامج قادر على تنفيذ عدة أوامر مع بعض في وقت واحد, و هو يملك عدة دوال و خصائص تجعلك قادراً على التحكم بطريقة تنفيذ هذه الأوامر.
كل كائن نوعه Thread تقوم بإنشائه, يكون الهدف منه تنفيذ مجموعة أوامر أثناء تنفيذ أوامر أخرى.
كل كائن Thread تقوم بإنشاءه, يتم إعطاءه رقم تعرفة ( id ) و إسم ( name ) خاص فيه, بالتالي يمكن الوصول لكائن الـ Thread من خلال رقم التعرفة أو الإسم الذي أعطي له.
بالإضافة إلى ذلك, يملك كل كائن Thread رقم بين 1 و 10 يحدد أولية التنفيذ ( Priority ).
كائن الـ Thread الذي يملك أعلا Priority يتم تنفيذ أوامره في الأول. لذلك, تلاعب بهذا الرقم فقط في حال كان عندك كائن Thread أهم من غيره.
تلقائياً, أي كائن Thread تقوم بإنشائه, يتم إعطائه Priority تساوي 5 حتى يتم تنفيذ جميع أوامر كائنات الـ Thread بشكل متوازي و عادل.
بناؤه
بما أن الكلاس Thread يطبق الإنترفيس Runnable, فهذا يؤكد أنهما مصممان لنفس الغرض, و هو جعل البرنامج قادراً على تنفيذ عدة أوامر مع بعض في وقت واحد.
خطوات بناء Thread بواسطة الكلاس Thread
- نفعل extends للكلاس Thread.
- نفعل Override للدالة run() لنضع جميع الأوامر التي نريدها أن تتنفذ عند تشغيل كائن الـ Thread.
مثال
خطوات إنشاء الـ Thread و تشغيله
- ننشئ كائن من الكلاس الذي يرث من الكلاس Thread.
- نستدعي الدالة start() لتشغيل كائن الـ Thread.
مثال
في حال قمت بتشغل كائن الـ Thread بواسطة الدالة run() بدل الدالة start(), عندها ستعامل الدالة run() كدالة عادية, أي سيتم إيقاف باقي الأوامر الموجودة في الدالة main() حتى يتم تنفيذ الأوامر الموجودة بداخل الدالة run() ثم العودة للدالة main() لمتابعة تنفيذ باقي الأوامر الموجودة فيها.
الحالات التي يمر بها الـ Thread (Thread States)
عندما تتعامل مع الـ Thread فأنت بذلك تقوم بتغيير حالته, فمرة تنشئه, و مرة تجهزه و تشغله, و قد توقفه عن العمل مدة معينة ثم تعيده للعمل بعد مدة, و عند الإنتهاء يتم مسحه من الذاكرة لتوفير مساحة التخزين.
في الجدول التالي ذكرنا الحالات التي يمر بها الـ Thread.
الحالة | معناها |
---|---|
NEW | هذه الحالة تعني أن كائن الـ Thread قد تم إنشاءه في الذاكرة. |
RUNNABLE | هذه الحالة تعني أن كائن الـ Thread أصبح جاهزاً و يمكن تنفيذ أوامره. |
TIMED_WAITING | هذه الحالة تعني أن كائن الـ Thread قد تم إيقافه عن التنفيذ لمدة معينة. |
BLOCKED | هذه الحالة تعني أن كائن الـ Thread موقوف لحين تنفيذ أوامر Thread آخر. |
WAITING | هذه الحالة تعني أن كائن الـ Thread تم إيقافه عن التنفيذ لبعض الوقت ريثما ينتهي تنفيذ أوامر Thread آخر. |
TERMINATED | هذه الحالة تعني أن كائن الـ Thread تم إيقافه نهائياً, و تم مسحه من الذاكرة. |
ملاحظة: بعد أن تصبح حالة كائن الـ Thread تساوي TERMINATED يمكن إعطاء رقم التعرفة و إسم الكائن السابق لكائن Thread آخر.
كونستركتورات الكلاس Thread
في الجدول التالي ذكرنا جميع كونستركتورات الكلاس Thread.
الكونستركتور مع تعريفه | |
---|---|
1 | public Thread() ينشئ كائن نوعه Thread ليس له إسم محدد. |
2 | public Thread(String name) ينشئ كائن نوعه Thread مع تحديد إسمه. |
3 | public Thread(Runnable target) ينشئ كائن نوعه Thread من كائن نوعه Runnable. |
4 | public Thread(Runnable target, String name) ينشئ كائن نوعه Thread من كائن نوعه Runnable مع تحديد إسمه. |
5 | public Thread(ThreadGroup group, String name)
ينشئ كائن نوعه Thread و يضعه ضمن مجموعة محددة, مع تحديد إسمه. باراميترات
يرمي الإستثناء SecurityException في حال كان كائن الـ Thread الحالي لا يملك صلاحية إنشاء كائن Thread جديد بداخل المجموعة المشار إليها. |
6 | public Thread(ThreadGroup group, Runnable target)
ينشئ كائن نوعه Thread من كائن نوعه Runnable و يضعه ضمن مجموعة محددة. باراميترات
يرمي الإستثناء SecurityException في حال كان كائن الـ Thread الحالي لا يملك صلاحية إنشاء كائن Thread جديد بداخل المجموعة المشار إليها. |
7 | public Thread(ThreadGroup group, Runnable target, String name)
ينشئ كائن نوعه Thread من كائن نوعه Runnable و يضعه ضمن مجموعة محددة, مع تحديد إسمه. باراميترات
يرمي الإستثناء SecurityException في حال كان كائن الـ Thread الحالي لا يملك صلاحية إنشاء كائن Thread جديد بداخل المجموعة المشار إليها. |
8 | public Thread(ThreadGroup group, Runnable target, String name, long stackSize)
ينشئ كائن نوعه Thread من كائن نوعه Runnable و يضعه ضمن مجموعة محددة, مع تحديد إسمه, و تحديد المساحة القصوى التي يمكن أن تحجز له في الذاكرة. باراميترات
يرمي الإستثناء SecurityException في حال كان كائن الـ Thread الحالي لا يملك صلاحية إنشاء كائن Thread جديد بداخل المجموعة المشار إليها. |
دوال الكلاس Thread
الجدول التالي يحتوي على دوال الكلاس Thread.
الدالة مع تعريفها | |
---|---|
1 | public void run()
تستخدم لتجهيز الأوامر التي ستنفذ عند تشغيل الـ Thread. الكلاس الذي يرث من الكلاس Thread يجب أن يفعل لها Override, و يضع بداخلها الأوامر التي يريدها أن تتنفذ عند تشغيل الـ Thread. ملاحظة: لتشغيل الـ Thread عليك إستدعاء الدالة start() و التي بدورها تقوم باستدعاء الدالة run(), أي تنفذ الأوامر الموجودة فيها. |
2 | public void start()
تستخدم لتشغيل كائن الـ Thread, أي لتنفيذ الأوامر التي تم وضعها في الدالة run(). ترمي الإستثناء IllegalThreadStateException في حال كان كائن الـ Thread شغالا قبل استدعائها. |
3 | public static void sleep(long milliseconds)
توقف تنفيذ أوامر كائن الـ Thread الذي قام باستدعائها لمدة محددة. بعد إنتهاء هذه المدة يعود للعمل من جديد. milliseconds هو المدة التي سيتوقف فيها كائن الـ Thread بالـ Milli Seconds. فمثلاً إذا وضعت 1000 مكان هذا الباراميتر, سؤدي ذلك إلى إيقاف كائن الـ Thread عن التنفيذ لثانية واحدة. ترمي الإستثناء IllegalThreadStateException في حال تم إعطاءها قيمة أصغر من 0. ترمي الإستثناء InterruptedException في حال تم إيقاف كائن الـ Thread في فترة إنتظاره. |
4 | public static void suspend()
توقف تنفيذ أوامر كائن الـ Thread الذي قام باستدعائها لمدة غير محددة, بحيث لا يمكنه العودة للعمل من جديد إلا إذا قام باستدعاء الدالة resume() بعدها. ترمي الإستثناء SecurityException في حال كان كائن الـ Thread الحالي لا يملك صلاحية التعديل على كائن الـ Thread المتوقف عن العمل. |
5 | public static void resume()
تكمل تنفيذ أوامر كائن الـ Thread بعد أن كان قد توقف عن تنفيذها بسبب الدالة suspend(). ترمي الإستثناء SecurityException في حال كان كائن الـ Thread الحالي لا يملك صلاحية التعديل على كائن الـ Thread المتوقف عن العمل. |
6 | public static void stop()
توقف كائن الـ Thread الذي قام باستدعائها, بحيث لا يمكنه العودة للعمل من جديد. ترمي الإستثناء SecurityException في حال كان كائن الـ Thread الحالي لا يملك صلاحية التعديل على كائن الـ Thread المتوقف عن العمل. |
7 | public final void join()
تجعل البرنامج لا يتابع تنفيذ الأوامر الموجودة في الدالة main() حتى يتم تنفيذ جميع الأوامر الموجودة في كائن الـ Thread الذي قام باستدعائها, أي حتى تصبح حالته تساوي TERMINATED. ملاحظة: لا يهمها إذا كان يوجد أكثر من كائن Thread آخر شغال في نفس الوقت. بمعنى أنه إذا كان هناك كائن Thread آخر شغال فإنه سيستمر في العمل بشكل طبيعي, لكن باقي الأوامر الموجودة في الدالة main() لن يتم تنفيذهم حتى تنفيذ جميع أوامر كائن الـ Thread الذي قام باستدعائها. ترمي الإستثناء InterruptedException في حال تم إيقاف كائن الـ Thread من قبل أي كائن Thread آخر. |
8 | public void interrupt()
ترسل إشارة إلى الـ JVM لمقاطعة عمل كائن الـ Thread في حال كان شغالاً. في حال كان كائن الـ Thread متوقفاً لمدة بسبب إحدى الدوال مثل join() و wait() و sleep(), فإنها لا تؤثر عليه أبداً. في حال كان كائن الـ Thread غير شغال بعد لا تؤثر عليه أيضاً. ترمي الإستثناء SecurityException في حال كان كائن الـ Thread الحالي لا يملك صلاحية التعديل على كائن الـ Thread المتوقف عن العمل. معلومة: قد تحتاج هذه الدالة في حال كنت تريد إيقاف كائن الـ Thread ريثما تنتهي من تنفيذ أوامر أخرى أكثر أهمية, في حال نجحت هذه الدالة تكون مدة إيقاف كائن الـ Thread عن العمل غير محددة. |
9 | public boolean isInterrupted()
تستخدم لمعرفة إذا كان كائن الـ Thread قد توقف بسبب استدعاء الدالة interrupt() أم لا. ترجع true في حال تم استدعاء الدالة interrupt() قبلها و قد تمكنت هذه الأخيرة من مقاطعة كائن الـ Thread أثناء تنفيذ الأوامر. غير ذلك ترجع false. |
10 | public long getId() ترجع رقم الـ ID الذي تم إعطاءه لكائن الـ Thread. |
11 | public Thread.State getState() ترجع كائن نوعه Thread.State يمثل حالة كائن الـ Thread الذي قام باستدعائها. |
12 | public final boolean isAlive()
ترجع true في حال لم يكن كائن الـ Thread الذي قام باستدعائها في الحالة TERMINATED. غير ذلك ترجع false. |
13 | public String toString()
ترجع نص يمثل معلومات كائن الـ Thread الذي قام باستدعائها. المعلومات عبارة عن ( Group - Priority - Name ).
|
14 | public final void setName(String name)
تستخدم لإعطاء إسم لكائن الـ Thread. ترمي الإستثناء SecurityException في حال كان كائن الـ Thread الحالي لا يملك صلاحية تغييرها. |
15 | public final String getName() ترجع إسم كائن الـ Thread. |
16 | public final int setPriority(int newPriority)
تستخدم لتغيير قيمة الـ Priority المعطاة لكائن الـ Thread.
ترمي الإستثناء IllegalArgumentException في حال كانت القيمة التي قمت بتمريرها أصغر من 1 أو أكبر من 10. و ترمي الإستثناء SecurityException في حال كان كائن الـ Thread الحالي لا يملك صلاحية تغييرها. |
17 | public final int getPriority() ترجع رقم بين 1 و 10 عبارة عن قيمة الـ Priority التي يملكها كائن الـ Thread الذي قام باستدعائها. |
18 | public final void setDaemon(boolean on)
تستخدم لتحويل كائن الـ Thread الذي قام باستدعائها إلى Daemon Thread. في حال قمت بتمرير القيمة true لها كـ Argument, سيتم معاملة كائن الـ Thread كــ Daemon Thread غير ذلك سيتم إعتباره Thread عادي. ترمي الإستثناء IllegalArgumentException في حال كان كائن الـ Thread شغالا أثناء استدعاءها. و ترمي الإستثناء SecurityException في حال كان كائن الـ Thread الحالي لا يملك صلاحية تغييرها. |
19 | public final boolean isDaemon()
تستخدم لمعرفة إذا كان كائن الـ Thread الذي قام باستدعائها هو Daemon Thread أم لا. ترجع true إذا كان كذلك, غير ذلك ترجع false. |
20 | public static int activeCount()
تستخدم لمعرفة عدد الـ threads الذين يعملون في البرنامج لحظة استدعائها. تستخدم فقط في حالة الـ Debugging. ملاحظة: في حال لم يكن هناك أي Thread شغال أو معرف في البرنامج, فإنها أيضاً ترجع 1 و الذي هو عبارة عن البرنامج نفسه. |
21 | public static Thread currentThread() ترجع كائن نوعه Thread يشير إلى كائن الـ Thread الذي قام باستدعائها. |
22 | public final ThreadGroup getThreadGroup()
ترجع كائن نوعه ThreadGroup يمثل المجموعة التي ينتمي إليها كائن الـ Thread الذي قام باستدعائها. ترجع true في حال كانت حالة كائن الـ Thread تساوي TERMINATED. |
أمثلة شاملة
المثال الأول
المثال التالي عبارة عن برنامج يعرض الوقت الحالي للجهاز.
في البداية قمنا بإنشاء كلاس إسمه RealTime يرث من الكلاس Thread.
بعدها فعلنا Override للدالة run() لجعلها تطبع الوقت الحالي.
ثم قمنا بإنشاء كلاس إسمه Main لتجربة هذا الـ Thread.
في الكلاس Main قمنا بإنشاء كائن من الكلاس RealTime ثم قمنا بتشغيله بواسطة الدالة start().
عند تشغيل البرنامج, سيتم عرض الوقت الحالي كل ثانية كالتالي.
المثال الثاني
المثال التالي عبارة عن برنامج لإختبار قدرة المستخدم في العمليات الحسابية, فهو يقوم بخلق عمليات جمع عشوائية خلال مدة معينة, و إنتظار المستخدم للإجابة عليها, و في الأخير سيتم عرض النتيجة النهائية له.
في البداية قمنا بإنشاء كلاس إسمه ExamTimer يرث من الكلاس Thread.
بعدها فعلنا Override للدالة run() حتى نجعل أي كائن من الكلاس ExamTimer ينتظر مدة 20 ثانية بعد تشغيله, ثم يتوقف مباشرةً عن العمل.
ثم قمنا بإنشاء كلاس إسمه Main, و الذي سيستخدم الكلاس ExamTimer كمؤقت.
في الكلاس Main فعلنا الأشياء التالية:
- أنشأنا كائن من الكلاس ExamTimer إسمه et.
- قمنا بتشغيل الكائن et بواسطة الدالة start().
- قمنا بتعريف المتغيرات num1 و num2 لتخزين الأرقام العشوائية التي سيتم توليدها في البرنامج.
- قمنا بتعريف المتغير userAnswer لتخزين العدد الذي سيدخله المستخدم في كل مرة.
- قمنا بتعريف المتغيرات operationsCounter, correctAnswersCounter و wrongAnswersCounter كعدادات في البرنامج.
operationsCounter: لتخزين عدد العمليات التي تظهر أمام المستخدم.
correctAnswersCounter: لتخزين عدد إجابات المستخدم الصحيحة.
wrongAnswersCounter: لتخزين عدد إجابات المستخدم الخاطئة. - قمنا بتعريف حلقة while تستمر في توليد أرقام العشوائية, إعداد عمليات جمع و إنتظار المستخدم لإدخال الإجابة بالإعتماد على الدالة isAlive() التي تبقي الحلقة تعيد تنفيذ الأوامر الموجودة فيها طالما أن مدة الإنتظار المحددة للكائن et غير منتهية بعد.
عند تشغيل البرنامج, سنحصل على نتيجة تشبه النتيجة التالية عند التشغيل.
الأرقام التي قمنا بتعليمها باللون الأصفر هي التي قمنا بإدخالها عند تجربة البرنامج.
0 + 8 = 8
5 + 7 = 11
8 + 8 = 16
6 + 6 = 12
3 + 9 = 12
7 + 4 = 14
9 + 4 = 13
Time end..
--------- Result --------
Number of operations: 7
Number of correct answers: 5
Number of wrong answers: 2
المثال الثالث
في المثال التالي قمنا تشغيل أكثر من كائن Thread في وقت واحد. الهدف هنا جعلك تدرك أن الأوامر لا تتنفذ فعلياً في وقت واحد لكنها تتنفذ بسرعة عالية جداً تجعل المستخدم يظن أنها تحدث في وقت واحد.
في البداية قمنا بإنشاء كلاس إسمه MyThread يرث من الكلاس Thread.
بعدها فعلنا Override للدالة run() حتى نجعل أي كائن من الكلاس MyThread يطبع إسمه كل ثانية.
ثم قمنا بإنشاء كلاس إسمه Main, و الذي قمنا فيه بإنشاء ثلاث كائنات من الكلاس MyThread و تشغيلهم مع بعض.
عند تشغيل البرنامج, نلاحظ أنه يطبع ثلاثة أسطر كل ثانية .
المثال الرابع
المثال التالي هو نفسه المثال السابق لكننا قمنا باستخدام الدالة run() بدلاً من الدالة start() حتى تعرف الفرق الحقيقي بينهما و ضرورة تشغيل كائن الـ Thread بواسطة الدالة start().
في البداية قمنا بإنشاء كلاس إسمه MyThread يرث من الكلاس Thread.
بعدها فعلنا Override للدالة run() حتى نجعل أي كائن من الكلاس MyThread يطبع إسمه ثم ينتظر مدة ثانية قبل أن يطبعه من جديد.
ثم قمنا بإنشاء كلاس إسمه Main, و الذي قمنا فيه بإنشاء ثلاث كائنات من الكلاس MyThread و تشغيلهم بواسطة الدالة run().
لاحظ كيف سيبقى البرنامج ينفذ أوامر أول كائن من الكلاس MyThread لأن الدالة run() تجعل البرنامج لا ينفذ أوامر أكثر من كائن Thread في وقت واحد.
عند تشغيل البرنامج, نلاحظ أنه يطبع ثلاثة أسطر كل ثانية .
المثال الخامس
في المثال التالي قمنا بإنشاء برنامج يظهر أمام المستخدم لائحة فيها أربع خيارات: تشغيل صوت التنبيه, إيقافه, إعادته للعمل, إيقافه نهائياً.
ميزة هذا البرنامج أنه يمكن تشغيل الإنذار أو إيقافه في نفس الوقت الذي يطلب فيه من المستخدم إدخال رقم الخيار.
في البداية قمنا بإنشاء كلاس إسمه Alert يرث من الكلاس Thread.
بعدها فعلنا Override للدالة start() لأننا لا نريد جعل البرنامج يعلق في حال قام المستخدم باستدعاء الدالة start() مرة ثانية.
فعلياً قلنا أن أي إستدعاء جديد للدالة start() بعد إستدعائها في المرة الأولى سيتم إستدعاء الدالة resume() بدلاً منها.
بعدها فعلنا Override للدالة run() حتى نجعل أي كائن من الكلاس Alert يصدر صوت تنبيه عند تشغيله.
ثم قمنا بإنشاء كلاس إسمه Main لتجربة الكلاس Alert.
عند تشغيل البرنامج ظهر لك أربع خيارات: تشغيل صوت التنبيه, إيقافه, إعادته للعمل, إيقافه نهائياً.
ستلاحظ أن البرنامج قادر على تشغيل صوت التنبيه في نفس الوقت الذي يطلب فيه من المستخدم إدخال رقم الخيار.
إنتبه: إرفع صوت الحاسوب إلا أعلا مستوى حتى تسمع صوت التنبيه عند تشغيله.
قم بتشغيل البرنامج و أدخل نفس القيم التي قمنا بتعليمها باللون الأصفر لتلاحظ كيف يعمل.
لا تنسى رفع صوت حاسوبك إلى أعلا مستوى حتى تسمع صوت التنبيه و هو يعمل.
| Enter (1) to Start |
| Enter (2) to Pause |
| Enter (3) to Resume |
| Enter (4) to Stop |
-----------------------
User input >> 1
User input >> 2
User input >> 3
User input >> 2
User input >> 3
User input >> 4