implements Runnable أم extends Thread: دليلك لاجتياز مقابلة Java

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

implements Runnable vs extends Thread: لماذا يخطئ 90% من مطوري Java؟

\n\n

تخيل أنك في مقابلة عمل بإحدى شركات التقنية الكبرى. يبتسم مسؤول الموارد البشرية ويطرح سؤالاً "بسيطاً": "ما الفرق بين implements Runnable و extends Thread في Java؟". قد يبدو الأمر سهلاً، لكن وفقاً لبيانات Stack Overflow، يتعثر حتى المطورون ذوو الخبرة هنا. آلاف المشاهدات ومئات التعليقات — موضوع "Java Runnable vs Thread" يظل من أكثر المواضيع إثارة للجدل.

\n\n

في هذا المقال، لن نكتفي بسرد الفروقات فقط. بل سنحلل أهم 5 أخطاء حقيقية يقع فيها المبرمجون، ونوضح لك كيفية كتابة كود تعدد مهام نظيف وقابل للتوسع. إذا كنت تكتب بلغة Java وتستخدم Thread مباشرة — اقرأ بتركيز. فقد تكون تخلق مشاكل لنفسك دون أن تدري.

\n\n

مقدمة سريعة: ما هما Thread و Runnable؟

\n\n

قبل أن نتعمق، دعنا نراجع الأساسيات. Java Thread هو كلاس يمثل مسار تنفيذ. يمكنه تشغيل كود بشكل متوازٍ. Runnable هو واجهة وظيفية (Functional Interface) تحتوي على دالة واحدة فقط هي run(). تصف المهمة التي يجب تنفيذها، لكنها لا تعرف كيفية تشغيلها.

\n\n

إليك مثال تقليدي باستخدام extends Thread:

\n\n
public class MyThread extends Thread {\n    @Override\n    public void run() {\n        System.out.println("الـ Thread يعمل: " + Thread.currentThread().getName());\n    }\n\n    public static void main(String[] args) {\n        MyThread t = new MyThread();\n        t.start(); // تشغيل Thread جديد\n    }\n}
\n\n

وهذا نفس الوظيفة باستخدام implements Runnable:

\n\n
public class MyTask implements Runnable {\n    @Override\n    public void run() {\n        System.out.println("المهمة تُنفذ: " + Thread.currentThread().getName());\n    }\n\n    public static void main(String[] args) {\n        Thread t = new Thread(new MyTask());\n        t.start();\n    }\n}
\n\n

للوهلة الأولى، الفرق بسيط. لكن الشيطان، كما يُقال، يكمن في التفاصيل.

\n\n

أهم 5 أخطاء عند العمل مع Thread و Runnable

\n\n

الخطأ رقم 1: وراثة Thread عندما تحتاج إلى Runnable

\n\n

المشكلة الأكثر شيوعاً هي الارتباط الصارم بكلاس Thread. عندما تكتب extends Thread، يصبح كلاسك هو نفسه Thread. وبالتالي تفقد القدرة على وراثة كلاس آخر. Java لغة ذات وراثة مفردة، وهذا قيد كبير.

\n\n

مثال على كود سيء:

\n\n
public class DataProcessor extends Thread {\n    // لم يعد بإمكانك extends JFrame, ArrayList أو أي كلاس مفيد آخر\n    @Override\n    public void run() {\n        // معالجة البيانات\n    }\n}
\n\n

الحل الصحيح: استخدم implements Runnable. إنها واجهة — يمكنك تطبيقها في أي كلاس يرث شيئاً آخر بالفعل.

\n\n
public class DataProcessor extends BaseService implements Runnable {\n    @Override\n    public void run() {\n        // معالجة البيانات\n    }\n}
\n\n

الخطأ رقم 2: الخلط بين run() و start()

\n\n

كلاسيكي. المبتدئون (وحتى غيرهم) غالباً ما يستدعون run() بدلاً من start(). الفرق جوهري: start() ينشئ Thread جديداً ويستدعي run() داخله. أما run() فيُنفذ في الـ Thread الحالي كدالة عادية.

\n\n

مثال على الخطأ:

\n\n
Thread t = new Thread(new MyTask());\nt.run(); // يعمل، لكن في نفس الـ Thread! لا يوجد تعدد مهام.
\n\n

سيكون الإخراج في الـ Thread الرئيسي main، وليس في Thread جديد. هذا الخطأ صعب الاكتشاف لأن الكود لا يتعطل — لكنه ببساطة لا يفعل ما تتوقعه.

\n\n

كيف تتجنبه: استخدم دائماً start() لتشغيل Thread جديد. إذا كنت في شك

المدونات

توصيات الكتب