قوائم المهام في Java: ExecutorService و ThreadPoolExecutor
في تطبيقات تعدد الخيوط بلغة Java، تُعد إدارة المهام وتنفيذها من المهام الرئيسية. بدلاً من إنشاء خيط جديد لكل مهمة (وهو أمر مكلف وغير فعال)، يستخدم المطورون قوائم المهام (Task Queues) مع تجمعات الخيوط. يتيح ذلك التحكم في عدد الخيوط العاملة في وقت واحد، وإعادة استخدامها، ومعالجة الأحمال القصوى بسلاسة.
الأساس للعمل مع قوائم المهام في Java هو إطار العمل java.util.concurrent، الذي تم تقديمه في Java 5. المكونات الرئيسية: ExecutorService، ThreadPoolExecutor، وتطبيقات مختلفة لـ BlockingQueue.
1. ExecutorService — واجهة عالية المستوى
ExecutorService هي واجهة توفر طرقًا لتنفيذ المهام بشكل غير متزامن. تخفي تفاصيل إدارة الخيوط. الطريقة الأكثر شيوعًا للحصول على مثيل منها هي استخدام طرق المصنع الخاصة بالفئة Executors:
ExecutorService executor = Executors.newFixedThreadPool(4); // تجمع من 4 خيوطexecutor.submit(() -> { System.out.println("المهمة تُنفذ في الخيط: " + Thread.currentThread().getName());});executor.shutdown(); // الإنهاء بعد تنفيذ جميع المهامومع ذلك، فإن طرق المصنع لها إمكانيات تكوين محدودة. للتكوين الدقيق، يتم استخدام ThreadPoolExecutor مباشرة.
2. ThreadPoolExecutor — تجمع خيوط قابل للتخصيص
ThreadPoolExecutor هو جوهر إطار العمل. يقبل قائمة المهام، سياسة التشبع، وقت بقاء الخيوط، ومعلمات أخرى. المُنشئ الكامل:
public ThreadPoolExecutor( int corePoolSize, // الحد الأدنى لعدد الخيوط int maximumPoolSize, // الحد الأقصى لعدد الخيوط long keepAliveTime, // وقت خمول الخيط قبل إزالته TimeUnit unit, // وحدة الوقت BlockingQueue<Runnable> workQueue, // قائمة المهام RejectedExecutionHandler handler // سياسة عند الامتلاء);مثال لإنشاء تجمع بقائمة مهام مخصصة:
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, 4, 30, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.AbortPolicy() // افتراضيًا — رمي استثناء);executor.execute(() -> { // مهمة ثقيلة});executor.shutdown();3. BlockingQueue — أساس قائمة المهام
نوع قائمة المهام يحدد كيفية تخزين المهام ونقلها إلى الخيوط. التطبيقات الأكثر شيوعًا:
LinkedBlockingQueue— قائمة غير محدودة تعتمد على قائمة مرتبطة. إذا لم يتم تعيين السعة، فقد يؤدي ذلك إلى تجاوز الذاكرة.ArrayBlockingQueue— قائمة محدودة تعتمد على مصفوفة. تسمح بالتحكم في عدد المهام المنتظرة.SynchronousQueue— قائمة بسعة صفرية. يتم نقل كل مهمة مباشرة إلى الخيط، ولا يوجد انتظار في القائمة.PriorityBlockingQueue— قائمة ذات أولويات. يتم فرز المهام حسب الأولوية (تطبقComparable).
مثال بقائمة محدودة وسياسة رفض:
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, 5, 10, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.CallerRunsPolicy() // المهمة تُنفذ في الخيط المستدعي);for (int i = 0; i < 20; i++) { executor.submit(() -> { Thread.sleep(100); return Math.random(); });}executor.shutdown();4. سياسات التشبع (RejectedExecutionHandler)
عندما تمتلئ القائمة