تحتوي Java على مؤشر ترابط خاص، وهو الخيط الخفي، الذي له أولوية منخفضة جدًا ولن يتم تنفيذه إلا عندما لا يتم تنفيذ سلاسل الرسائل الأخرى في نفس البرنامج.
نظرًا لأن الخيوط الخفية تتمتع بهذه الخصائص، فإنها تُستخدم عمومًا لتقديم خدمات للخيوط العادية (وتسمى أيضًا خيوط المستخدم) في البرنامج. لديهم بشكل عام حلقة لا نهائية، أو يتم استخدامها لانتظار خدمات الطلب، أو لأداء المهام، وما إلى ذلك. لا يمكنهم القيام بأي عمل مهم لأننا لسنا متأكدين من متى سيتم تخصيص وقت وحدة المعالجة المركزية لهم، وسوف تنتهي تلقائيًا عند عدم تنفيذ أي سلاسل رسائل أخرى. التطبيق النموذجي لهذا النوع من الخيوط هو مجموعة Java المهملة.
في المثال الموجود في هذا القسم، سنقوم بإنشاء خيطين، أحدهما خيط عادي يكتب الأحداث في قائمة الانتظار؛ والآخر عبارة عن خيط خفي، يقوم بمسح الأحداث في قائمة الانتظار وحذف الأحداث الموجودة لأكثر من 10 ثوانٍ.
تعرف عليه
اتبع الخطوات أدناه لتنفيذ نموذج البرنامج.
1. قم بإنشاء فئة حدث، والتي تستخدم فقط لحفظ معلومات الحدث المطلوبة لتنفيذ البرنامج. قم بتعريف خاصيتين، إحداهما هي خاصية التاريخ من النوع java.util.Date، والأخرى هي خاصية الحدث من النوع String، ثم قم بإنشاء طرق القراءة والكتابة لهاتين الخاصيتين. الرمز هو كما يلي:
انسخ رمز الكود كما يلي:
حدث الطبقة العامة {
تاريخ التاريخ الخاص؛
حدث سلسلة خاص؛
التاريخ العام getDate() {
تاريخ العودة؛
}
مجموعة الفراغ العام (تاريخ التاريخ) {
this.date = date;
}
سلسلة عامة getEvent () {
حدث العودة؛
}
مجموعة باطلة عامة (حدث سلسلة) {
this.event = events;
}
}
2. قم بإنشاء فئة باسم WriterTask وقم بتنفيذ الواجهة القابلة للتشغيل. الرمز هو كما يلي:
انسخ رمز الكود كما يلي:
فئة عامة WriterTask تنفذ Runnable {
3. قم بتعريف سمة قائمة الانتظار المستخدمة لتخزين الأحداث، وتنفيذ مُنشئ الفئة، واستخدام معلماتها لتهيئة سمة قائمة الانتظار. الرمز هو كما يلي:
انسخ رمز الكود كما يلي:
خاص Deque<Event> deque;
public WriterTask(Deque<Event> deque) {
this.deque = deque;
}
4. قم بتنفيذ طريقة التشغيل () لهذه المهمة، والتي تحتوي على حلقة تجتاز 100 مرة. في كل اجتياز، يتم إنشاء كائن حدث جديد، ثم يتم حفظه في قائمة الانتظار، ويظل في وضع السكون لمدة ثانية واحدة. الرمز هو كما يلي:
انسخ رمز الكود كما يلي:
@تجاوز
تشغيل الفراغ العام () {
لـ (int i = 0; i < 100; i++) {
حدث الحدث = حدث جديد ()؛
Event.setDate(new Date());
Event.setEvent(String.format("أنشأ مؤشر الترابط %s حدثًا"،
Thread.currentThread().getId()));
deque.addFirst(event);
يحاول {
TimeUnit.SECONDS.sleep(1);
} قبض على (InterruptedException e) {
printStackTrace();
}
}
}
5. قم بإنشاء فئة باسم CleanerTask وارث فئة Thread. الرمز هو كما يلي:
انسخ رمز الكود كما يلي:
الطبقة العامة CleanerTask تمتد الموضوع {
6. قم بتعريف سمة قائمة الانتظار المستخدمة لتخزين الأحداث، وتنفيذ مُنشئ الفئة، واستخدام معلماتها لتهيئة سمة قائمة الانتظار. في طريقة البناء، قم بتعيين الخيط كخيط خفي عن طريق استدعاء طريقة setDaemon (). الرمز هو كما يلي:
انسخ رمز الكود كما يلي:
خاص Deque<Event> deque;
public CleanerTask(Deque<Event> deque) {
this.deque = deque;
setDaemon(true);
}
7. قم بتنفيذ طريقة التشغيل () هناك حلقة لا نهائية في نص الطريقة للحصول على الوقت الحالي ثم استدعاء طريقة Clearn (). الرمز هو كما يلي:
انسخ رمز الكود كما يلي:
@تجاوز
تشغيل الفراغ العام () {
بينما (صحيح) {
تاريخ التاريخ = تاريخ جديد ()؛
نظيف(تاريخ);
}
}
8. قم بتنفيذ الطريقة النظيفة (). في هذه الطريقة، احصل على آخر مرة، ثم تحقق من فارق التوقيت بين الوقت والوقت الحالي. إذا تم إنشاؤه قبل 10 ثوانٍ، فاحذف الحدث الحالي، ثم تحقق من الحدث التالي حدث. إذا تم حذف حدث ما، فستتم طباعة المعلومات المتعلقة بالحدث المحذوف، كما سيتم طباعة آخر طول لقائمة الانتظار، بحيث يمكن ملاحظة تقدم تنفيذ البرنامج. الرمز هو كما يلي:
انسخ رمز الكود كما يلي:
تنظيف الفراغ الخاص (تاريخ التاريخ) {
فرق طويل؛
حذف منطقي؛
إذا (deque.size() == 0) {
يعود؛
}
حذف = خطأ؛
يفعل {
الحدث e = deque.getLast();
الفرق = date.getTime() - e.getDate().getTime();
إذا (الفرق > 10000) {
System.out.printf("المنظف: %s/n"، e.getDate());
deque.removeLast();
حذف = صحيح؛
}
} بينما (الفرق > 10000)؛
إذا (حذف) {
System.out.printf("المنظف: حجم قائمة الانتظار: %d/n"، deque.size());
}
}
9. قم بإنشاء الفئة الرئيسية للبرنامج، الفئة الرئيسية، ثم قم بتنفيذ الطريقة الرئيسية (). الرمز هو كما يلي:
انسخ رمز الكود كما يلي:
الطبقة العامة الرئيسية {
public static void main(String[] args) {
10. استخدم فئة Deque لإنشاء قائمة انتظار لتخزين الأحداث. الرمز هو كما يلي:
انسخ رمز الكود كما يلي:
Deque<Event> deque = new ArrayDeque<>();
11. قم بإنشاء وبدء تشغيل ثلاثة سلاسل WriterTask وسلسلة رسائل CleanerTask واحدة. الرمز هو كما يلي:
انسخ رمز الكود كما يلي:
Deque<Event> deque = new ArrayDeque<>();
WriterTaskwriter = new WriterTask(deque);
لـ (int i = 0; i < 3; i++) {
موضوع الموضوع = موضوع جديد (كاتب)؛
Thread.start();
}
CleanerTask Cleaner = new CleanerTask(deque);
Cleaner.start();
12. قم بتنفيذ البرنامج وعرض نتائج التنفيذ.
أعرف لماذا
وبتحليل نتائج تنفيذ البرنامج، يمكننا أن نرى أن قائمة الانتظار تزيد أولاً إلى 30، ثم تتغير بين 27 و30 حتى انتهاء تنفيذ البرنامج.
يبدأ البرنامج أولاً في التنفيذ من ثلاثة سلاسل رسائل لـ WriterTask، ويضيف كل مؤشر ترابط أولاً حدثًا إلى قائمة الانتظار ثم ينام لمدة ثانية واحدة. بعد مرور الثواني العشرة الأولى، سيكون هناك ثلاثون حدثًا في قائمة الانتظار. خلال هذه الثواني العشر، عندما تكون جميع سلاسل WriterTask الثلاثة في وضع السكون، سيتم أيضًا تشغيل مؤشر ترابط CleanerTask، ولكن لن يتم حذف أي أحداث لأن وقت إنشاء جميع الأحداث لا يتجاوز 10 ثوانٍ. بعد أول 10 ثوانٍ، تضيف ثلاث مهام Writer ثلاثة أحداث إلى قائمة الانتظار كل ثانية، وبالمثل، تقوم CleanerTask بحذف ثلاثة أحداث كل ثانية. وبالتالي فإن عدد الأحداث يتراوح بين 27 و 30.
عندما تكون جميع سلاسل WriterTask في وضع السكون، تكون لدينا الحرية في معالجة الوقت، مما يسمح للخيط الخفي بالعمل خلال هذا الوقت. إذا قمت بتعيين وقت سكون خيط WriterTask ليكون أقصر، فسيحصل خيط CleanerTask على وقت تشغيل أقل لوحدة المعالجة المركزية. إذا كان الأمر كذلك، فسيستمر طول قائمة الانتظار في النمو لأن مؤشر ترابط CleanerTask لا يحصل أبدًا على وقت تشغيل كافٍ لحذف أحداث كافية.
لا تنتهي أبدا
لا يمكن تعيين الخيط كخيط خفي إلا عن طريق استدعاء طريقة setDaemon() قبل استدعاء طريقة start(). بمجرد بدء تشغيل مؤشر الترابط، لا يمكن تعديل حالة البرنامج الخفي.
يمكنك أيضًا استخدام isDaemon() للتحقق مما إذا كان الخيط عبارة عن خيط خفي. إذا كان خيطًا خفيًا، فإنه يُرجع صحيحًا؛ وإذا كان خيطًا عاديًا، فإنه يُرجع خطأ.
استخدام العقيدة
تمت ترجمة هذه المقالة من "Java 7 Concurrency Cookbook" (سرقها D Gua Ge باسم "مجموعة أمثلة Java7 Concurrency") ويتم استخدامها فقط كمواد تعليمية. ولا يجوز استخدامه لأية أغراض تجارية دون تصريح.
نجاح صغير
إصدارات كاملة من كافة نماذج التعليمات البرمجية المستخدمة في هذا القسم.
رمز كامل لفئة الحدث
انسخ رمز الكود كما يلي:
الحزمة com.diguage.books.concurrencycookbook.chapter1.recipe7؛
import java.util.Date;
/**
* فئة معلومات الحدث
* التاريخ: 2013-09-19
*الزمن: 22:56
*/
حدث الطبقة العامة {
تاريخ التاريخ الخاص؛
حدث سلسلة خاص؛
التاريخ العام getDate() {
تاريخ العودة؛
}
مجموعة الفراغ العام (تاريخ التاريخ) {
this.date = date;
}
سلسلة عامة getEvent () {
حدث العودة؛
}
مجموعة باطلة عامة (حدث سلسلة) {
this.event = events;
}
}
رمز كامل لفئة WriterTask
انسخ رمز الكود كما يلي:
الحزمة com.diguage.books.concurrencycookbook.chapter1.recipe7؛
import java.util.Date;
import java.util.Deque;
import java.util.concurrent.TimeUnit;
/**
* إنشاء حدث كل ثانية.
* التاريخ: 2013-09-19
*الزمن: 22:59
*/
فئة عامة WriterTask تنفذ Runnable {
خاص Deque<Event> deque;
public WriterTask(Deque<Event> deque) {
this.deque = deque;
}
@تجاوز
تشغيل الفراغ العام () {
لـ (int i = 0; i < 100; i++) {
حدث الحدث = حدث جديد ()؛
Event.setDate(new Date());
Event.setEvent(String.format("أنشأ مؤشر الترابط %s حدثًا"،
Thread.currentThread().getId()));
deque.addFirst(event);
يحاول {
TimeUnit.SECONDS.sleep(1);
} قبض على (InterruptedException e) {
printStackTrace();
}
}
}
}
الكود الكامل لفئة CleanerTask
انسخ رمز الكود كما يلي:
الحزمة com.diguage.books.concurrencycookbook.chapter1.recipe7؛
import java.util.Date;
import java.util.Deque;
/**
* تنظيف الحدث
* التاريخ: 2013-09-19
*الزمن: 23:33
*/
الطبقة العامة CleanerTask تمتد الموضوع {
خاص Deque<Event> deque;
public CleanerTask(Deque<Event> deque) {
this.deque = deque;
setDaemon(true);
}
@تجاوز
تشغيل الفراغ العام () {
بينما (صحيح) {
تاريخ التاريخ = تاريخ جديد ()؛
نظيف(تاريخ);
}
}
/**
* حذف الحدث.
*
* @تاريخ المعلمة
*/
تنظيف الفراغ الخاص (تاريخ التاريخ) {
فرق طويل؛
حذف منطقي؛
إذا (deque.size() == 0) {
يعود؛
}
حذف = خطأ؛
يفعل {
الحدث e = deque.getLast();
الفرق = date.getTime() - e.getDate().getTime();
إذا (الفرق > 10000) {
System.out.printf("المنظف: %s/n"، e.getDate());
deque.removeLast();
حذف = صحيح؛
}
} بينما (الفرق > 10000)؛
إذا (حذف) {
System.out.printf("المنظف: حجم قائمة الانتظار: %d/n"، deque.size());
}
}
}
الكود الكامل للفئة الرئيسية
انسخ رمز الكود كما يلي:
الحزمة com.diguage.books.concurrencycookbook.chapter1.recipe7؛
import java.util.ArrayDeque;
import java.util.Deque;
/**
* التاريخ: 2013-09-19
*الزمن: 23:54
*/
الطبقة العامة الرئيسية {
public static void main(String[] args) {
Deque<Event> deque = new ArrayDeque<>();
WriterTaskwriter = new WriterTask(deque);
لـ (int i = 0; i < 3; i++) {
موضوع الموضوع = موضوع جديد (كاتب)؛
Thread.start();
}
CleanerTask Cleaner = new CleanerTask(deque);
Cleaner.start();
}
}