ExecutorService وأنواع تجمعات الخيوط
أنشئ تجمعات خيوط ثابتة ومخزّنة ومفردة ومجدولة، وافهم متى تستخدم كل نوع
ExecutorService وأنواع تجمعات الخيوط درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.
لماذا نستخدم مجموعات خيوط التنفيذ؟
إنشاء Thread جديد لكل مهمة مكلف — إذ ينطوي إنشاء خيوط التنفيذ وإتلافها على كلفة إضافية. تعيد مجموعة خيوط التنفيذ استخدام مجموعة ثابتة من الخيوط لتنفيذ مهام متعددة، مما يقلل الكلفة الإضافية ويتحكم في استهلاك الموارد.
import java.util.concurrent.*;
// Bad practice: new thread per task
new Thread(() -> System.out.println("task")).start();
// Better: reuse threads from a pool
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> System.out.println("task from pool"));
pool.shutdown();newFixedThreadPool
ينشئ مجموعة تضم N من الخيوط بالضبط. وتنتظر المهام الإضافية في قائمة الانتظار حتى يتوفر خيط. ويُعد مناسبًا عندما تعرف مستوى التوازي المطلوب.
ExecutorService pool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
final int taskId = i;
pool.submit(() -> {
System.out.println("Task " + taskId + " on " + Thread.currentThread().getName());
});
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);newCachedThreadPool
ينشئ الخيوط عند الطلب ويعيد استخدام الخيوط الخاملة. تُنهى الخيوط التي تظل خاملة لمدة 60 ثانية. ويُعد مناسبًا لكثير من المهام قصيرة العمر، لكنه محفوف بالمخاطر مع المهام طويلة الأمد أو غير المحدودة، إذ قد يستنفد الذاكرة.
ExecutorService cached = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
cached.submit(() -> {
// quick task
double result = Math.sqrt(Math.random());
});
}
cached.shutdown();newSingleThreadExecutor
مجموعة تضم خيطًا واحدًا بالضبط. تُنفَّذ المهام بالتتابع وبترتيب إرسالها. وتُعد مناسبة لتسلسل الوصول إلى مورد مشترك.
ExecutorService single = Executors.newSingleThreadExecutor();
single.submit(() -> System.out.println("Task 1"));
single.submit(() -> System.out.println("Task 2"));
single.submit(() -> System.out.println("Task 3"));
// Always prints: Task 1, Task 2, Task 3 (sequential)
single.shutdown();newScheduledThreadPool
يدعم المهام المجدولة والمتكررة. استخدمه للاستطلاع وإعادة المحاولة وأعمال الصيانة في الخلفية.
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// One-shot delay
scheduler.schedule(() -> System.out.println("Delayed!"),
3, TimeUnit.SECONDS);
// Fixed rate (every 2 seconds)
scheduler.scheduleAtFixedRate(
() -> System.out.println("Tick"),
0, 2, TimeUnit.SECONDS);
// Run later: scheduler.shutdown();VirtualThreadExecutor (Java 21+)
تقدم Java 21 خيوطًا افتراضية — خفيفة الوزن ومنخفضة التكلفة عند إنشائها. استخدم Executors.newVirtualThreadPerTaskExecutor() لأعباء العمل الكثيفة بعمليات الإدخال والإخراج:
// Java 21+
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
final int id = i;
exec.submit(() -> {
Thread.sleep(100); // blocks without tying up OS thread
System.out.println("vthread " + id);
});
}
} // auto-shutdownاختيار المجموعة المناسبة
إرشادات:
- المهام المقيّدة بالمعالج: مجموعة ثابتة مع N = عدد أنوية المعالج
- المهام المقيّدة بعمليات الإدخال والإخراج: مجموعة مخزَّنة أو خيوط افتراضية
- المهام التسلسلية: منفّذ بخيط واحد
- المهام المجدولة أو المتكررة: مجموعة خيوط مجدولة
int cores = Runtime.getRuntime().availableProcessors();
ExecutorService cpuPool = Executors.newFixedThreadPool(cores);
System.out.println("Pool size: " + cores);ThreadPoolExecutor مخصّص
للحصول على تحكم دقيق، أنشئ ThreadPoolExecutor مباشرةً:
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
2, // corePoolSize
10, // maximumPoolSize
60L, // keepAliveTime
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100), // work queue
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // rejection policy
);
tpe.submit(() -> System.out.println("Custom pool task"));
tpe.shutdown();إيقاف التشغيل بصورة سليمة
أوقِف ExecutorService دائمًا لتحرير الخيوط. يتوقف shutdown() عن قبول المهام الجديدة، لكنه يُكمل المهام الموجودة في قائمة الانتظار. ويحاول shutdownNow() إلغاء المهام قيد التنفيذ.
ExecutorService pool = Executors.newFixedThreadPool(4);
// ... submit tasks ...
pool.shutdown(); // no new tasks
try {
if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
pool.shutdownNow(); // force cancel
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}ExecutorService باعتباره AutoCloseable (Java 19+)
في Java 19 والإصدارات الأحدث، يطبّق ExecutorService الواجهة AutoCloseable. استخدم try-with-resources للإيقاف التلقائي:
// Java 19+ (preview), Java 21 stable
try (ExecutorService pool = Executors.newFixedThreadPool(4)) {
pool.submit(() -> System.out.println("task"));
} // pool.close() called: waits for tasks then shuts downسياسات الرفض
عندما تصل المجموعة إلى سعتها القصوى (تكون جميع الخيوط مشغولة وتمتلئ قائمة الانتظار)، تتولى سياسة الرفض معالجة الفائض:
- AbortPolicy (الافتراضية): ترمي
RejectedExecutionException - CallerRunsPolicy: ينفّذ خيط المستدعي المهمة
- DiscardPolicy: تتجاهل المهمة بصمت
- DiscardOldestPolicy: تتجاهل أقدم مهمة في قائمة الانتظار
اختبار سريع
لديك مهمة مقيّدة بالمعالج وتنفّذ حسابات مكثفة. كم خيطًا يجب أن تضم مجموعة الخيوط الثابتة؟
مراجعة: ExecutorService ومجموعات خيوط التنفيذ
أهم النقاط:
- المجموعة الثابتة: عدد ثابت N من الخيوط؛ وتنتظر المهام الإضافية في قائمة الانتظار
- المجموعة المخزَّنة: تنمو عند الطلب؛ وتنتهي صلاحية الخيوط الخاملة بعد 60 ثانية
- الخيط الواحد: يضمن ترتيب التنفيذ التسلسلي
- المجموعة المجدولة: للمهام المؤجلة والمتكررة
- استدعِ shutdown() دائمًا؛ واستخدم awaitTermination() للخروج بصورة سليمة
الأسئلة الشائعة
هل درس «ExecutorService وأنواع تجمعات الخيوط» مجاني؟
نعم — نص درس «ExecutorService وأنواع تجمعات الخيوط» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.
ماذا ستتعلم في «ExecutorService وأنواع تجمعات الخيوط»؟
أنشئ تجمعات خيوط ثابتة ومخزّنة ومفردة ومجدولة، وافهم متى تستخدم كل نوع تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟
لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «ExecutorService وأنواع تجمعات الخيوط»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟
نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- ExecutorService وأنواع تجمعات الخيوط
- إرسال المهام: Runnable مقابل Callable
- Future ومعالجة الأخطاء
- ScheduledExecutorService للمهام المتكررة