عندما يبدأ الكود الخاص بك في التلعثم: مقدمة إلى قوائم المهام والمهام الخلفية في Java
\n\nتخيل الموقف: تقوم بتشغيل خادم ويب بلغة Java. يأتي طلب من مستخدم - تحتاج إلى إرسال بريد إلكتروني، وإنشاء تقرير PDF، وتحديث الإحصائيات. إذا قمت بتنفيذ كل شيء بشكل متزامن، سينتظر المستخدم 30 ثانية. سيبرد قهوته، وسيذهب إلى المنافسين. مألوف؟
\n\nفي عالم المهام الخلفية في Java يتم حل هذا بأناقة: نلقي بالمهمة في قائمة الانتظار ونعيد للمستخدم "200 OK". ثم، في الخلفية، يقوم العامل (worker) الخاص بنا بمعالجة رسائل البريد الإلكتروني وملفات PDF والإحصائيات بهدوء. لا فوضى، فقط نظام.
\n\nاليوم سنشرح كيفية تنفيذ قوائم المهام في Java - من أبسط إصدار أحادي الخيط إلى الحلول الصناعية مع RabbitMQ و Kafka. ستتعلم كيف تتوقف عن إطفاء الحرائق وتبدأ في تصميم أنظمة موثوقة.
\n\nلماذا نحتاج إلى قوائم المهام؟ ثلاثة أسباب ستفاجئك
\n\n- \n
- عدم التزامن - المستخدم لا ينتظر. سرعة استجابة الخادم تنخفض من 30 ثانية إلى 5 ملي ثانية. \n
- موازنة التحميل - الطلبات الذروة لا تعطل الخادم. يتم تخزين المهام في قائمة الانتظار ومعالجتها مع تحرر الموارد. \n
- تحمل الأخطاء - إذا تعطل عامل واحد، تبقى المهمة في قائمة الانتظار. عامل آخر سيلتقطها. لا يتم فقدان البيانات. \n
في Java، توجد أدوات مدمجة لهذا (ExecutorService، BlockingQueue) ووسطاء خارجيون (RabbitMQ، Kafka). يعتمد الاختيار على النطاق. لنبدأ من الصغير.
قائمة مهام بسيطة في Java: ExecutorService و BlockingQueue
\n\nإذا كنت لا تحتاج إلى نظام موزع، وتريد فقط نقل العمل الثقيل إلى الخلفية - استخدم ThreadPoolExecutor. هذا هو مجمع خيوط مدمج يأخذ المهام من قائمة الانتظار.
مثال: مجمع خيوط لمعالجة الطلبات
\n\nimport java.util.concurrent.*;\n\npublic class OrderProcessor {\n private final ExecutorService executor;\n\n public OrderProcessor(int threadCount) {\n // إنشاء مجمع بعدد ثابت من الخيوط\n this.executor = Executors.newFixedThreadPool(threadCount);\n }\n\n public void processOrder(Order order) {\n executor.submit(() -> {\n // عمل ثقيل: إنشاء إيصال، إرسال بريد إلكتروني، تحديث المخزون\n System.out.println("معالجة الطلب: " + order.getId());\n try {\n Thread.sleep(2000); // محاكاة العمل\n } catch (InterruptedException e) {\n Thread.currentThread().interrupt();\n }\n System.out.println("الطلب " + order.getId() + " تمت معالجته");\n });\n }\n\n public void shutdown() {\n executor.shutdown();\n }\n\n public static void main(String[] args) {\n OrderProcessor processor = new OrderProcessor(4);\n for (int i = 0; i < 10; i++) {\n processor.processOrder(new Order(i));\n }\n processor.shutdown();\n }\n}\n\nclass Order {\n private final int id;\n public Order(int id) { this.id = id; }\n public int getId() { return id; }\n}\n\nماذا يحدث هنا؟ أنشأنا مجمعًا من 4 خيوط. سيتم معالجة 10 طلبات بمعدل 4 طلبات بالتوازي. تتم إدارة قائمة مهام Java داخليًا بواسطة ThreadPoolExecutor - وهي LinkedBlockingQueue افتراضيًا. عندما تكون جميع الخيوط مشغولة، تنتظر المهام دورها في قائمة الانتظار.
إعداد متقدم: الأولويات وتحمل الأخطاء
\n\nماذا لو كانت المهام عاجلة؟ استخدم PriorityBlockingQueue:
BlockingQueue<Runnable> priorityQueue = new PriorityBlockingQueue<>(\n 10,\n Comparator.comparingInt(task -> ((PriorityTask) task).getPriority())\n);\n\nThreadPoolExecutor executor = new ThreadPoolExecutor(\n 2, 4, 60, TimeUnit.SECONDS, priorityQueue\n);\n\nالآن المهام ذات الأولوية العالية