سؤال

من أي وقت قضيته في المواضيع في جاوة، لقد وجدت هذه الطرقتين لكتابة المواضيع:

مع implements Runnable:

public class MyRunnable implements Runnable {
    public void run() {
        //Code
    }
}
//Started with a "new Thread(new MyRunnable()).start()" call

أو مع extends Thread:

public class MyThread extends Thread {
    public MyThread() {
        super("MyThread");
    }
    public void run() {
        //Code
    }
}
//Started with a "new MyThread().start()" call

هل هناك أي فرق كبير في هذه الكتلتين من التعليمات البرمجية؟

هل كانت مفيدة؟

المحلول

نعم: تنفذ Runnable هي الطريقة المفضلة للقيام بذلك، المنظمة البحرية الدولية. أنت لست متخصصا في سلوك الخيط. أنت فقط تعطيها شيئا لتشغيله. هذا يعني تعبير هل فلسفيا طريقة "أنقى" للذهاب.

في عملي شروط، وهذا يعني أنه يمكنك تنفيذ Runnable وتمتد من فئة أخرى أيضا.

نصائح أخرى

TL؛ DR: تنفذ Runnable أفضل. ومع ذلك، التحذير مهم

بشكل عام، أود أن أوصي باستخدام شيء مثل Runnable بدلا من Thread لأنه يسمح لك بالحفاظ على عملك فقط إلى جانب اختيارك من التزامن. على سبيل المثال، إذا كنت تستخدم Runnable وتقرر في وقت لاحق أن هذا لا يحتاج إلى ذلك Thread, ، يمكنك فقط استدعاء Threata.run ().

مذكرة قانونية: حول هنا، أنا تثبيط بقوة استخدام المواضيع الخام. أنا أفضل بكثير استخدام callables. و futuretasks. (من Javadoc: "حساب غير متزامن للإلغاء"). إن دمج المهلات والإلغاء السليم والتجمع لخيط دعم التزامن الحديث أكثر فائدة بالنسبة لي أكثر من أكوام المواضيع الخام.

متابعة: هناك FutureTask البناء تتيح لك ذلك استخدام Runnables (إذا كان هذا هو الأكثر راحة معه) وما زال من الاستفادة من أدوات التزامن الحديثة. إلى اقتباس جافادوك:

إذا لم تكن بحاجة إلى نتيجة معينة، ففكر في استخدام إنشاءات النموذج:

Future<?> f = new FutureTask<Object>(runnable, null)

لذلك، إذا استبدلنا runnable مع الخاص بك threadA, ، نحصل على ما يلي:

new FutureTask<Object>(threadA, null)

خيار آخر يسمح لك بالبقاء أقرب إلى Runnables هو threadpoolexecutor.. وبعد يمكنك استعمال ال نفذ - اعدم الطريقة لتمرير في تنفذ "المهمة المعينة في وقت ما في المستقبل".

إذا كنت ترغب في محاولة استخدام تجمع مؤشر ترابط، فسيصبح جزء التعليمات أعلاه شيءا مثل ما يلي (باستخدام Executors.newcachedtreadpool () طريقة المصنع):

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());

المغزى من القصة:

يرث فقط إذا كنت ترغب في تجاوز بعض السلوك.

أو بالأحرى يجب أن تقرأ باسم:

يرث أقل، واجهة أكثر.

حسنا الكثير من الإجابات الجيدة، أريد أن أضيف المزيد عن هذا. هذا سوف يساعد في فهم Extending v/s Implementing Thread.
يمتد يربط ملفين فئتين عن كثب ويمكن أن يسبب بعض الصعب جدا التعامل مع التعليمات البرمجية.

كلا النهجين تفعل نفس الوظيفة ولكن كانت هناك بعض الاختلافات.
الفرق الأكثر شيوعا هو

  1. عند تمديد فئة الخيط، بعد ذلك، لا يمكنك تمديد أي فئة أخرى تتطلبها. (كما تعلمون، لا تسمح جافا بوريث أكثر من فئة واحدة).
  2. عند تنفيذ Runnable، يمكنك توفير مساحة لفصلك لتوسيع أي فئة أخرى في المستقبل أو الآن.

ومع ذلك، واحد فرق واضح بين تنفيذ رونابل وموسعة الخيط هو ذلك
by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance.

المثال التالي سوف يساعدك على فهم أكثر وضوحا

//Implement Runnable Interface...
 class ImplementsRunnable implements Runnable {

private int counter = 0;

public void run() {
    counter++;
    System.out.println("ImplementsRunnable : Counter : " + counter);
 }
}

//Extend Thread class...
class ExtendsThread extends Thread {

private int counter = 0;

public void run() {
    counter++;
    System.out.println("ExtendsThread : Counter : " + counter);
 }
}

//Use the above classes here in main to understand the differences more clearly...
public class ThreadVsRunnable {

public static void main(String args[]) throws Exception {
    // Multiple threads share the same object.
    ImplementsRunnable rc = new ImplementsRunnable();
    Thread t1 = new Thread(rc);
    t1.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    Thread t2 = new Thread(rc);
    t2.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    Thread t3 = new Thread(rc);
    t3.start();

    // Creating new instance for every thread access.
    ExtendsThread tc1 = new ExtendsThread();
    tc1.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    ExtendsThread tc2 = new ExtendsThread();
    tc2.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    ExtendsThread tc3 = new ExtendsThread();
    tc3.start();
 }
}

إخراج البرنامج أعلاه.

ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1

في نهج واجهة Runnable، يتم إنشاء مثيل واحد فقط من الفصل وتم مشاركتها بواسطة مؤشرات ترابط مختلفة. لذلك يتم زيادة قيمة العداد لكل وصول إلى الخيوط.

في حين أن نهج فئة الموضوع، يجب أن تضطر إلى إنشاء مثيل منفصل لكل وصول من الخيط. وبالتالي يتم تخصيص الذاكرة المختلفة لكل حالات فئة ولكل منها عداد منفصل، تظل القيمة هي نفسها، مما يعني أنه لن يحدث زيادة لأن أي من مرجع الكائن هو نفسه.

متى تستخدم Runnable؟
استخدم واجهة Runnable عندما ترغب في الوصول إلى نفس الموارد من مجموعة المواضيع. تجنب استخدام فئة مؤشر الترابط هنا، لأن إنشاء كائنات متعددة تستهلك ذاكرة أكثر ويصبح مرفقا كبيرا للأداء.

فئة تنفذ Runnable ليست مؤشر ترابط وفقط فئة. للحصول على Runnable ليصبح موضوعا، تحتاج إلى إنشاء مثيل من الخيط ويمر نفسه كهدف.

في معظم الحالات، يجب استخدام واجهة Runnable إذا كنت تخطط فقط لتجاوز run() الطريقة ولا طرق موضوع أخرى. هذا مهم لأن الفصول يجب ألا تكون الفئات ما لم تعتزم المبرمون تعديل أو تعزيز السلوك الأساسي للفئة.

عندما تكون هناك حاجة إلى تمديد Superclass، فإن تطبيق الواجهة الرافحة أكثر ملاءمة من استخدام فئة مؤشر الترابط. لأنه يمكننا تمديد فئة أخرى أثناء تنفيذ واجهة Runnable لإنشاء مؤشر ترابط.

آمل أن يساعد هذا!

الشيء الوحيد الذي فوجئته لم يذكر ذلك هو أن التنفيذ Runnable يجعل الفصل الخاص بك أكثر مرونة.

إذا قمت بتمديد مؤشر الترابط، فسيكون الإجراء الذي تقوم به دائما في مؤشر ترابط. ومع ذلك، إذا قمت بتنفيذ Runnable لا يجب أن يكون. يمكنك تشغيله في مؤشر ترابط، أو قم بتمريره إلى نوع من خدمة المنفذين، أو قم فقط بتمريره كقوة داخل تطبيق واحد مترابط (ربما يجب تشغيله في وقت لاحق، ولكن في نفس الخيط). الخيارات أكثر انفتاحا إذا كنت تستخدم فقط Runnable مما لو كنت ترتبط نفسك Thread.

إذا كنت ترغب في تنفير أو تمديد أي فئة أخرى إذن Runnable الواجهة هي الأكثر تفضيلا، وإلا، إذا كنت لا ترغب في تمديد أي فئة أخرى أو تنفيذها بعد ذلك Thread الطبقة الأفضل.

الفرق الأكثر شيوعا هو

enter image description here

عندما انت extends Thread فئة، بعد ذلك لا يمكنك تمديد أي فئة أخرى التي لا تحتاج إليها. (كما تعلمون، لا تسمح جافا بوريث أكثر من فئة واحدة).

عندما انت implements Runnable, ، يمكنك توفير مساحة لفصلك لتوسيع أي فئة أخرى في المستقبل أو الآن.

  • لا تدعم جافا المتعردات المتعددة، مما يعني أنه يمكنك فقط تمديد فئة واحدة فقط في جافا، لذلك بمجرد موسم فئة مؤشر الترابط، فقدت فرصتك ولا تستطيع تمديد أو يرث فئة أخرى في جافا.

  • في البرمجة الموجهة للكائنات، تمديد الفصل يعني بشكل عام، إضافة وظيفة جديدة، وتعديل السلوكيات أو تحسينها. إذا لم نقم بإجراء أي تعديل على مؤشر الترابط، فاستخدم واجهة Runnable بدلا من ذلك.

  • تمثل واجهة Runnable مهمة يمكن تنفيذها إما عن طريق الخيط أو المنفصين العاديين أو أي وسيلة أخرى. الانفصال المنطقي للمهمة باعتباره Runnable من الخيط قرار تصميم جيد.

  • فصل المهمة بأنها Runnable تعني يمكننا إعادة استخدام المهمة ولديها أيضا الحرية لتنفيذها من وسائل مختلفة. نظرا لأنه لا يمكنك إعادة تشغيل مؤشر ترابط بمجرد اكتماله. مرة أخرى Runnable vs الموضوع للمهمة، Runnable هو الفائز.

  • يدرك مصمم Java هذا وهذا هو السبب في أن المنفزين يقبلون Runnable كمهمة ولديهم مؤشر ترابط عامل ينفذ هذه المهمة.

  • ورث جميع طرق الخيط هو النفقات العامة الإضافية فقط لتمثيل المهمة التي يمكن القيام بها بسهولة مع Runnable.

من باب المجاملة javarevisited.blogspot.com.

كانت هذه بعض الاختلافات البارزة بين الخيط وروح في جافا. إذا كنت تعرف أي اختلافات أخرى حول الخيط مقابل Runnable من فضلك مشاركته عبر التعليقات. أنا شخصيا استخدم Runnable على مؤشر الترابط لهذا السيناريو وتوصي باستخدام واجهة Runnable أو Callable بناء على الاحتياجات الخاصة بك.

ومع ذلك، فإن الفرق الكبير هو.

عندما انت extends Thread فئة، يخلق كل من مؤشر ترابطك كائنا فريدا وربما معه. عندما انت implements Runnable, ، يشارك نفس الكائن إلى مؤشرات الترابط متعددة.

في الواقع، ليس من الحكمة المقارنة Runnable و Thread مع بعض.

هذا اثنان لديه الاعتماد والعلاقة في الخيوط المتعددة تماما مثل Wheel and Engine علاقة السيارات.

أود أن أقول، هناك طريقة واحدة فقط لمتعددة الخيوط مع خطوتين. اسمحوا لي أن تجعل وجهة نظري.

Runnable:
عند التنفيذ interface Runnable هذا يعني أنك تخلق شيئا ما run able في مؤشر ترابط مختلف. الآن إنشاء شيء يمكن تشغيله داخل مؤشر ترابط (Runnable داخل مؤشر ترابط)، لا يعني إنشاء مؤشر ترابط.
لذلك الفصل MyRunnable ليس سوى الطبقة العادية مع void run طريقة. وستكون الكائنات بعض الكائنات العادية بطريقة فقط run والتي سوف تنفذ بشكل طبيعي عند الاتصال. (ما لم نمر الكائن في مؤشر ترابط).

خيط:
class Thread, ، أود أن أقول فئة خاصة جدا مع إمكانية بدء موضوع جديد تمكن بالفعل متعدد الخيوط من خلال start() طريقة.

لماذا لا حكيم للمقارنة؟
لأننا نحتاج إلى كل منهما لمتعدد الخيوط.

بالنسبة للخيوط المتعددة، نحتاج إلى شيئين:

  • شيء يمكن تشغيله داخل مؤشر ترابط (Runnable).
  • شيء يمكن أن يبدأ موضوع جديد (موضوع).

لذلك من الناحية الفنية والمنظرية كلاهما ضروري لبدء موضوع، سوف واحد يركض ويلد اجعلها تعمل (يحب Wheel and Engine من السيارات).

لهذا السبب لا يمكنك بدء موضوع مع MyRunnable تحتاج إلى تمريرها إلى مثال Thread.

ولكن من الممكن إنشاء وتشغيل مؤشر ترابط فقط باستخدام class Thread لأن الفصل Thread تنفذ Runnable لذلك نعلم جميعا Thread أيضا هو Runnable في داخل.

أخيرا Thread و Runnable تكمل لبعضها البعض لمكثتها غير منافس أو استبدال.

يجب عليك تطبيق Runnable، ولكن إذا كنت تعمل على Java 5 أو أعلى، يجب ألا تبدأ ذلك new Thread ولكن استخدام an. ExecutorService. في حين أن. لمزيد من التفاصيل: كيفية تنفيذ الخيوط البسيطة في جافا.

أنا لست خبيرا، لكن يمكنني التفكير في أسباب واحد لتنفيذ Runnable بدلا من تمديد الخيط: Java يدعم فقط الميراث الوحيد، حتى تتمكن من تمديد فئة واحدة فقط.

تحرير: قال هذا في الأصل "تنفيذ واجهة يتطلب موارد أقل". كذلك، ولكن تحتاج إلى إنشاء مثيل مؤشر ترابط جديد في كلتا الحالتين، لذلك كان هذا خطأ.

أود أن أقول أن هناك طريقة ثالثة:

public class Something {

    public void justAnotherMethod() { ... }

}

new Thread(new Runnable() {
   public void run() {
    instanceOfSomething.justAnotherMethod();
   }
}).start();

ربما يتأثر هذا قليلا من خلال استخدامي الغزير الأخير لجافا سكريبت و ActionScript 3، ولكن بهذه الطريقة لا يحتاج فئتك إلى تطبيق واجهة غامضة جميلة مثل Runnable.

مع إصدار Java 8، يوجد الآن خيار ثالث.

Runnable هو واجهة وظيفية, ، مما يعني أنه يمكن إنشاء مثيلات منه مع تعبيرات Lambda أو مراجع الأسلوب.

يجب استبدال مثالك ب:

new Thread(() -> { /* Code here */ }).start()

أو إذا كنت ترغب في استخدام ExecutorService ومرجع طريقة:

executor.execute(runner::run)

هذه ليست أقصر بكثير من الأمثلة الخاصة بك، ولكن تأتي أيضا مع العديد من المزايا المذكورة في إجابات أخرى من استخدام Runnable على Thread, ، مثل المسؤولية الفردية واستخدام التركيب لأنك غير متخصص في سلوك الخيط. هذه الطريقة تتجنب أيضا إنشاء فئة إضافية إذا كان كل ما تحتاجه هو Runnable كما تفعل في الأمثلة الخاصة بك.

Instantiating واجهة تعطي فصلا نظيفا بين التعليمات البرمجية وتنفيذ الخيوط، لذلك أفضل تنفيذ Runnable في هذه الحالة.

يبدو أن الجميع هنا يعتقدون أن تنفيذ الرونابل هو الطريق للذهاب ولا أوافق حقا معهم ولكن هناك أيضا حالة لتوسيع الخيط في رأيي، في الواقع لديك نوع من أظهرته في التعليمات البرمجية الخاصة بك.

إذا قمت بتنفيذ Runnable ثم الفئة التي تنفذ Runnable لا يوجد لديه سيطرة على اسم مؤشر الترابط، فهذا هو رمز الاتصال الذي يمكن تعيين اسم مؤشر الترابط، مثل ذلك:

new Thread(myRunnable,"WhateverNameiFeelLike");

ولكن إذا قمت بتمديد مؤشر الترابط، فأنت تحصل على إدارة هذا ضمن الفصل نفسه (تماما كما هو الحال في مثالا، فأرسم اسم الخيط "ترتيب"). في هذه الحالة أنت:

أ) قد يعطها اسما أكثر فائدة لأغراض تصحيح الأخطاء

ب) إجبار أن يتم استخدام هذا الاسم لجميع حالات تلك الفئة (إلا إذا كنت تتجاهل حقيقة أنه مؤشر ترابط وتفعل ما ورد أعلاه كما لو كان رافئا، لكننا نتحدث عن الاتفاقية هنا في أي حال تجاهل هذا الاحتمال أشعر).

قد تكون حتى على سبيل المثال، تتبع مكدس لإنشاءها واستخدامها كاسم مؤشر الترابط. قد يبدو هذا غريبا ولكن اعتمادا على كيفية تنظيم الكود الخاص بك يمكن أن يكون مفيدا للغاية لأغراض تصحيح الأخطاء.

قد يبدو هذا شيئا صغيرا ولكن حيث لديك تطبيق معقد للغاية مع الكثير من الخيوط وكل الأشياء المفاجئة "توقفت" (إما لأسباب توقف الجمود أو ربما بسبب عيب في بروتوكول شبكة سيكون أقل أسباب واضحة - أو غيرها من الأسباب التي لا نهاية لها) ثم احصل على تفريغ مكدس من Java حيث يتم استدعاء جميع المواضيع "موضوع -1"، "موضوع -2"، "Thread-3" ليست مفيدة للغاية للغاية (يعتمد على كيفية أهميتك منظم وما إذا كان يمكنك معرفة ذلك بشكل مفيد الذي هو فقط من خلال تتبع المكدس الخاص بهم - ليس ممكنا دائما إذا كنت تستخدم مجموعات من مؤشرات الترابط متعددة تعمل على تشغيل نفس التعليمات البرمجية).

بعد أن أقول أنه يمكنك بالطبع أيضا أن تفعل ما سبق بطريقة عامة عن طريق إنشاء امتداد لفئة الخيط التي تحدد اسمها إلى تتبع مكدس مكالمة إنشاءها ثم استخدام ذلك مع تطبيقات Runnable بدلا من فئة مؤشرات الخيط القياسية (انظر أدناه) ولكن بالإضافة إلى تتبع المكدس، قد يكون هناك مزيد من المعلومات المحددة السياق ستكون مفيدة في اسم مؤشر الترابط لتصحيح الأخطاء (مرجع إلى واحدة من العديد من قوائم الانتظار أو المقابس التي يمكن أن تعالجها على سبيل المثال في هذه الحالة التي قد تفضلها تمديد مؤشر ترابط خصيصا لهذه الحالة بحيث يمكنك الحصول على برنامج التحويل البرمجي (أو غيرها باستخدام المكتبات الخاصة بك) لتمرير معلومات معينة (مثل قائمة الانتظار / المقبس في السؤال) للاستخدام في الاسم).

إليك مثال على مؤشر الترابط العام مع تتبع مكدس المكدس باسمه:

public class DebuggableThread extends Thread {
    private static String getStackTrace(String name) {
        Throwable t= new Throwable("DebuggableThread-"+name);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(os);
        t.printStackTrace(ps);
        return os.toString();
    }

    public DebuggableThread(String name) {
        super(getStackTrace(name));
    }

    public static void main(String[] args) throws Exception {
        System.out.println(new Thread());
        System.out.println(new DebuggableThread("MainTest"));
    }
}

وهنا عينة من الناتج مقارنة الأسماين:

Thread[Thread-1,5,main]
Thread[java.lang.Throwable: DebuggableThread-MainTest
    at DebuggableThread.getStackTrace(DebuggableThread.java:6)
    at DebuggableThread.<init>(DebuggableThread.java:14)
    at DebuggableThread.main(DebuggableThread.java:19)
,5,main]

Runnable لأن:

  • يترك مزيدا من المرونة لتنفيذ الرون لتوسيع فئة أخرى
  • يفصل الشفرة من التنفيذ
  • يسمح لك بتشغيل Runnable من تجمع مؤشر ترابط أو مؤشر ترابط الحدث، أو بأي طريقة أخرى في المستقبل.

حتى لو كنت لا تحتاج إلى أي من هذا الآن، فيمكنك في المستقبل. نظرا لعدم وجود فائدة لتجاوز الخيط، فإن Runnable هو حلا أفضل.

نظرا لأن هذا موضوع شائع للغاية، فإن الإجابات الجيدة تنتشر في كل مكان وتعاملت في عمق كبير، شعرت به مبررة لتجميع الإجابات الجيدة من الآخرين في شكل أكثر إيجازا، لذلك لدى القادمين الجدد نظرة عامة سهلة مقدما:

  1. يمكنك عادة تمديد فئة لإضافة أو تعديل الوظائف. وبالتالي، إذا كنت لا تريد ل الكتابة فوق أي سلوك الموضوع, ، ثم استخدام runnable.

  2. في نفس الضوء، إذا كنت لا تحتاج ل يرث طرق الموضوع، يمكنك القيام به دون ذلك تكاليف غير مباشرة باستخدام Runnable.

  3. ميراث واحد: إذا قمت بتمديد الخيط، فلا يمكنك تمديد من أي فئة أخرى، لذلك إذا كان هذا هو ما تحتاج إلى القيام به، فعليك استخدام Runnable.

  4. إنه تصميم جيد لفصل منطق المجال من الوسائل التقنية، بهذا المعنى من الأفضل أن يكون لديك مهمة روعة عزل لك مهمة من لك عداء.

  5. يمكنك نفذ - اعدم نفس الرون الاعتراض عدة مرات, ، كائن موضوع، ومع ذلك، لا يمكن إلا أن تبدأ مرة واحدة فقط. (ربما السبب، لماذا يقبل المنفصلون الرواق، ولكن ليسوا مؤشراتون.)

  6. إذا قمت بتطوير مهمتك كارجع، لديك كل المرونة كيفية استخدامها الآن وفي المستقبل. وبعد يمكنك تشغيلها بشكل متزامن عبر المنفصين ولكن أيضا عبر الخيط. وما زلت يمكنك أيضا استخدام / الاتصال به غير متزامن داخل مؤشر الترابط نفسه تماما مثل أي نوع / كائن عادي آخر.

  7. هذا يجعل من الأسهل أيضا متفرق المنطق المهام والتزامن الجوانب في لك اختبارات الوحدة.

  8. إذا كنت مهتما بهذا السؤال، فقد تكون مهتما أيضا الفرق بين Callable و Roulnable.

الفرق بين توسيع الخيط وتنفيذ Runnable هي:

enter image description here

ويناقش هذا في أوراكل تحديد وبدء موضوع الدورة التعليمية:

أي من هذه التعابير يجب أن تستخدمها؟ أول المصطلح، الذي يستخدم كائنا رافئا، هو أكثر عام، لأن الكائن Runnable يمكنه تصنيف فئة أخرى غير مؤشر ترابط. المصطلح الثاني هو أسهل في الاستخدام في التطبيقات البسيطة، ولكنه محدود بحقيقة أن فئة المهام الخاصة بك يجب أن تكون سليل الخيط. يركز هذا الدرس على النهج الأول، الذي يفصل المهمة الروابة عن كائن الخيط الذي ينفذ المهمة. ليس فقط هذا النهج أكثر مرونة، لكنه قابل للتطبيق على Apis إدارة الخيوط الرفيعة المستوى المغطاة لاحقا.

بمعنى آخر، تنفيذ Runnable ستعمل في سيناريوهات حيث يمتد صفك فئة أخرى غير Thread. وبعد جافا لا يدعم الميراث المتعددة. أيضا، تمتد Thread لن يكون من الممكن عند استخدام بعض واجهات برمجة التطبيقات من إدارة الخيوط الرفيعة المستوى. السيناريو الوحيد حيث تمتد Thread الأفضل هو في تطبيق صغير لن يخضع للتحديثات في المستقبل. من الأفضل دائما تنفيذها Runnable لأنها أكثر مرونة حيث ينمو مشروعك. لن يكون لتغيير التصميم تأثير كبير حيث يمكنك تنفيذ العديد من الواجهات في جافا، ولكن فقط تمديد فئة واحدة فقط.

إذا لم أكن مخطئا، فمن الأفضل أو أقل

ما هو الفرق بين واجهة وفئة مجردة؟

يمتد مؤسسات "هو"العلاقة والواجهة توفر"لديه أ" الإمكانية.

تفضل تنفذ Rennable. :

  1. إذا لم يكن لديك لتمديد فئة الخيط وتعديل التنفيذ الافتراضي لشركة API
  2. إذا كنت تنفذ النار وننسى الأمر
  3. إذا كنت قد تمتد بالفعل فئة أخرى

تفضل "يمتد الموضوع" :

  1. إذا كان عليك تجاوز أي من هذه خيط الأساليب كما هو مدرج في صفحة وثائق أوراكل

عموما لا تحتاج إلى تجاوز سلوك الخيط. وبالتالي تنفذ Rennable. هو المفضل لمعظم الأوقات.

في ملاحظة مختلفة، باستخدام متقدم ExecutorService أو ThreadPoolExecutorService يوفر API أكثر مرونة ومراقبة.

إلقاء نظرة على هذا SE سؤال:

ExecutorService vs عارضة الصفحات

أبسط التفسير سيكون عن طريق التنفيذ Runnable يمكننا تعيين نفس الكائن إلى مؤشرات ترابط متعددة وكل منهما Thread يشارك نفس دول الكائن والسلوك.

على سبيل المثال، لنفترض أن هناك اثنين من الخيوط، الموضوع 1. يضع عدد صحيح في صفيف و الموضوع 2. يأخذ الأعداد الصحيحة من الصفيف عند ملء الصفيف. لاحظ أنه من أجل الموضوع 2. للعمل يحتاج إلى معرفة حالة الصفيف، سواء الموضوع 1. قد ملأها أم لا.

تنفيذ Runnable يتيح لك الحصول على هذه المرونة لمشاركة الكائن في حين extends Thread يجعلك لإنشاء كائنات جديدة لكل مؤشرات ترابط، لذلك يتم فقد أي تحديث يتم بواسطة Direct1 إلى Thread2.

يفصل فئة الخيط من التنفيذ Runnable أيضا إلى تجنب مشاكل المزامنة المحتملة بين مؤشر الترابط وطريقة التشغيل (). يعطي Runnable منفصل عموما مرونة أكبر بالطريقة التي يشار إليها رمز الرون وتنفيذها.

هذا هو س من صلب: مسؤولية واحدة.

أ مسلك يجسد تشغيل السياق (كما هو الحال في سياق التنفيذ: إطار المكدس، معرف الخيط، إلخ) من الإعدام غير المتزامن من قطعة من التعليمات البرمجية. الذي - التي قطعة من الكود من الناحية المثالية يجب أن يكون نفس التنفيذ، سواء متزامن أو غير متزامن.

إذا قمت بتجميعها معا في تنفيذ واحد، فإنك تعطي الكائن الناتج غير مرتبطه أسباب التغيير:

  1. التعامل مع الخيط في طلبك (أي استعلام وتعديل سياق التنفيذ)
  2. خوارزمية تنفذها قطعة من الكود (الجزء الرافح)

إذا كانت اللغة التي تستخدمها تدعم الفئات الجزئية أو الميراث المتعددة، فيمكنك فصل كل سبب في صفها الفائق الخاص بها، ولكنها تغلي بنفسها على أنها تكوين الكائنتين، لأن مجموعات الميزات لا تتداخل. هذا للنظرية.

في الممارسة العملية، بشكل عام، لا يحتاج البرنامج إلى حمل أكثر تعقيدا من اللازم. إذا كان لديك مؤشر ترابط واحد يعمل على مهمة محددة، دون تغيير هذه المهمة، فمن المحتمل أن هناك نقطة في جعل المهام منفصلة الفئات، ولا يزال رمزك أبسط.

في سياق جاوة, ، منذ المرفق هناك بالفعل, ، ربما من الأسهل أن تبدأ مباشرة مع الوقوف وحدها Runnable الطبقات، وتمرير حالاتهم ل Thread (أو Executor) الحالات. مرة واحدة تستخدم إلى هذا النمط، ليس من الصعب استخدامه (أو حتى قراءته) من حالة مؤشر ترابطات Runnable البسيطة.

أحد الأسباب التي ترغب في تنفيذها واجهة بدلا من تمديد فئة أساسية هي أنك تقوم بالفعل بتوسيع بعض الطبقة الأخرى. يمكنك فقط تمديد فئة واحدة فقط، ولكن يمكنك تنفيذ أي عدد من الواجهات.

إذا قمت بإعادة تقييم الخيط، فأنت تمنع بشكل أساسي منطقك من خلال أي موضوع آخر من "هذا". إذا كنت تريد فقط بعض خيط لتنفيذ المنطق الخاص بك، من الأفضل فقط تنفيذ Runnable.

إذا كنت تستخدم Runnable، فيمكنك حفظ المساحة للتمديد إلى أي من فصلك الآخر.

هل يمكننا إعادة زيارة السبب الأساسي الذي أردنا أن يتصرف صفنا ك Threadب لا يوجد سبب على الإطلاق، أردنا فقط تنفيذ مهمة، على الأرجح في وضع غير متزامن، وهو ما يعنيه بدقة أن تنفيذ المهمة يجب أن فرع من مؤشر ترابطنا الرئيسي والخيط الرئيسي إذا كان ينتهي مبكرا أو قد ينتظر للمسار المتفرع (المهمة).

إذا كان هذا هو الغرض بأكمله، فأين أرى حاجة مؤشر ترابط متخصص. يمكن تحقيق ذلك عن طريق التقاط مؤشر ترابط الخام من تجمع مؤشر الترابط في النظام وتعيين مهمتنا (قد يكون مثالا من صفنا) وهذا هو ذلك.

لذلك دعونا نطيع مفهوم عفوا واكتب فئة من النوع الذي نحتاجه. هناك العديد من الطرق للقيام بأشياء، مما يدفع ذلك بالطريقة الصحيحة.

نحتاج إلى مهمة، لذلك اكتب تعريف المهمة التي يمكن تشغيلها على مؤشر ترابط. لذلك استخدم Runnable.

دائما تذكر implements يستخدم خصيصا لنقل السلوك و extends يستخدم لنقل ميزة / الخاصية.

نحن لا نريد خاصية الخيط، بدلا من ذلك، نريد أن نتصرف صفنا كامرأة يمكن تشغيلها.

نعم، إذا قمت باستدعاء مكالمة Therma، فلن تحتاج إلى استدعاء طريقة البدء وطريقة التشغيل هي الاتصال بعد استدعاء فئة TherceA فقط. ولكن إذا استخدمت مكالمة ThressB، فاحتاج إلى ضروري لبدء الخيط لطريقة تشغيل المكالمة. إذا كان لديك أي مساعدة أخرى، ردا علي.

أجد أنه من المفيد الأكثر فائدة لاستخدام Runnable لجميع الأسباب المذكورة، لكن أحيانا أحيانا أرغب في تمديد مؤشر ترابط حتى أتمكن من إنشاء طريقة توقف مؤشر الترابط الخاص والاتصال بها مباشرة على مؤشر الترابط الذي قمت بإنشائه.

لا تدعم Java ميراثا متعددة لذلك إذا قمت بإنقلة فئة الخيط، فلن يتم تمديد فئة أخرى.

على سبيل المثال: إذا قمت بإنشاء تطبيق صغير، فيجب أن يمتد فئة التطبيق الصغير حتى الآن الطريقة الوحيدة لإنشاء مؤشر ترابط عن طريق تنفيذ واجهة Runnable

Runnable هي واجهة، في حين Thread هي فئة تنفذ هذه الواجهة. من وجهة نظر التصميم، يجب أن يكون هناك فصل نظيف بين كيفية تعريف المهمة وبين كيفية تنفيذها. السابق هو مسؤولية أ Runnalbe التنفيذ، والأخير هو وظيفة Thread صف دراسي. في معظم الحالات المنفذة Runnable هي الطريقة الصحيحة لمتابعة.

الفرق بين الخيط والرجال. إذا نحن نقوم بإنشاء مؤشر ترابط باستخدام فئة الخيط ثم عدد الخيط يساوي عدد الكائن الذي أنشأناه. إذا كنا نقوم بإنشاء مؤشر ترابط عن طريق تنفيذ واجهة Runnable، فيمكننا استخدام كائن واحد لإنشاء موضوع متعدد .SO يتم مشاركة كائن واحد بواسطة موضوع متعدد. سيستغرق الأمر ذاكرة أقل

اعتمادا على الشرط إذا كانت بياناتنا ليست SENSTIVE. لذلك يمكن مشاركتها بين موضوع متعدد يمكننا استخدام واجهة Runnable.

مضيفا سنتان هنا -دائما كلما كان ذلك ممكنا implements Runnable وبعد فيما يلي تحذير حول سبب عدم استخدامكextends Threadس

  1. من الناحية المثالية يجب عليك أبدا تمديد فئة الخيط؛ ال Thread يجب أن يتم الفصل finalوبعد على الأقل طرق لها مثل thread.getId(). وبعد يرى هذه مناقشة عن خطأ متعلقة بتوسيع Threadس.

  2. أولئك الذين يحبون حل الألغاز يمكن أن يروا تأثير جانبي آخر لتوسيع الخيط. سيقوم الرمز أدناه بطباعة رمز غير قابل للوصول عندما لا أحد يخلقها.

لطفا أنظر http://pastebin.com/bjknns2g..

public class WaitPuzzle {

    public static void main(String[] args) throws InterruptedException {
        DoNothing doNothing = new DoNothing();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        Thread.sleep(100);
        doNothing.start();
        while(true) {
            Thread.sleep(10);
        }
    }


    static class WaitForever extends  Thread {

        private DoNothing doNothing;

        public WaitForever(DoNothing doNothing) {
            this.doNothing =  doNothing;
        }

        @Override
        public void run() {
            synchronized (doNothing) {
                try {
                    doNothing.wait(); // will wait forever here as nobody notifies here
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Unreachable Code");
            }
        }
    }

    static class DoNothing extends Thread {

        @Override
        public void run() {
            System.out.println("Do Nothing ");
        }
    } 
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top