لماذا لا يتم استدعاء Run () على الفور عندما يتم استدعاء start () على كائن مؤشر ترابط في Java

StackOverflow https://stackoverflow.com/questions/2690207

  •  01-10-2019
  •  | 
  •  

سؤال

أو هو؟
لدي كائن موضوع من:

Thread myThread = new Thread(pObject);

عندما يكون Pobject كائن فئة يقوم بتنفيذ واجهة Runnable ، ثم لدي طريقة بدء تشغيل كائن مؤشر الترابط مثل SO:

myThread.start();

الآن ، ما أفهمه هو أنه عندما يتم استدعاء Start () ، يطلق JVM ضمنيًا (وعلى الفور) طريقة Run () التي قد يتم تجاوزها (كما هي في حالتي)

ومع ذلك ، في حالتي ، يبدو أن طريقة البداية () لا تسمى على الفور (حسب الرغبة) ولكن حتى يتم إكمال العبارات/الأساليب الأخرى من كتلة الاتصال أي إذا كان لدي طريقة بعد بدء () الاتصال مثل SO:

myThread.start();
doSomethingElse();

يتم تنفيذ dosomthingelse () قبل تشغيل طريقة التشغيل () على الإطلاق.
ربما أكون مخطئًا في الفرضية الأولية التي تسمى Run () دائمًا مباشرة بعد أن يتم استدعاء START (). الرجاء المساعدة! المطلوب مرة أخرى هو جعل التنفيذ Run () مباشرة بعد البدء (). شكرًا.

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

المحلول

الآن ، أفهم أنه عندما يتم استدعاء Start () ، يطلق JVM ضمنيًا (وعلى الفور) طريقة Run () ...

هذا غير صحيح. إنه يتصل ضمنيًا run(), ، لكن المكالمة لا تحدث بالضرورة على الفور.

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

لإجبار موضوعك على البدء على الفور (أو لتكون أكثر دقة ، لبدء التشغيل من قبل doSomethingElse()) ، تحتاج إلى القيام ببعض التزامن الصريح ؛ على سبيل المثال شيء مثل هذا:

    java.util.concurrent.CountDownLatch latch = new CountdownLatch(1);
    new Thread(new MyRunnable(latch)).start();
    latch.await(); // waits until released by the child thread.
    doSomethingElse();

أين

class MyRunnable implements Runnable {
    private CountDownLatch latch;
    MyRunnable (CountDownLatch latch) { this.latch = latch; }
    public void run() {
        doSomeStuff();
        latch.countDown(); // releases the parent thread
        doSomeMoreStuff();
    }
    ...
}

هناك طرق أخرى لتنفيذ التزامن باستخدام فئات التزامن ، أو جافا Mutex / Wait / Attify Primitives1. لكن التزامن الصريح بين الخيوط هو الطريقة الوحيدة لضمان السلوك الذي تحتاجه.

نلاحظ أن doSomething() سيتم اكتمال الاتصال في خيط الطفل قبل إصدار خيط الأصل ، لكن لا يمكننا أن نقول شيئًا عن ترتيب تنفيذ doSomethingElese() و doSomeMoreStuff(). (قد يركض المرء أمام الآخر والعكس صحيح ، أو قد يركض بالتوازي.)


1 - باستخدام wait / notify لا ينصح به ، ولكن قد يكون خيارك الوحيد إذا لم تكن واجهات برمجة التطبيقات للتزامن متوفرة ؛ على سبيل المثال على جافا لي.

نصائح أخرى

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

run() هو أول شيء في الكود الخاص بك الذي يقوم به مؤشر الترابط الجديد ، ولكن هناك بعض أعمال الإعداد التي يقوم بها الخيط الجديد أولاً ، وليس هناك ما يضمن أن أي قدر كبير من العمل سيقوم بواسطة الخيط الجديد قبل أن يستمر الخيط الأصلي للإتصال doSomethingElse().

أنت محق في التفكير في أنه لا توجد ضمانات هنا. إن وضع افتراضات حول سلوك الكود متعدد مؤشرات الترابط هو مصدر الكثير من الألم - حاول ألا تفعل ذلك!

عندما تتصل myThread.start(), ، يصبح موضوعك متاحًا للتنفيذ. ما إذا كانت ستحصل بالفعل على وحدة المعالجة المركزية ، وإلى متى - إنها تصل إلى جدولة OS. في الواقع ، الخاص بك run() قد يكون السيطرة على الفور ، ولكن فقدانه قبل أن يتمكن من فعل أي شيء يمكنك ملاحظته. الطريقة الوحيدة لضمان تنفيذ موضوعك ما تحتاجه من قبل doSomethingElse() هو استخدام التزامن الصريح.

لقد بدأت موضوعًا جديدًا. يعمل هذا الموضوع بالتوازي مع الخيط الذي بدأه حتى يكون الترتيب:

pObject.run();
doSomethingElse();

أو

doSomethingElse();
pObject.run();

أو على الأرجح ، سيكون هناك بعض التقاطع. pObject.run() قد يركض في منتصف doSomethingElse() أو العكس العكس أو سيبدأ واحد قبل الانتهاء الآخر وهلم جرا. من المهم أن نفهم هذا وفهم ما هو المقصود بالعملية الذرية أو ستجد نفسك مع بعض الأخطاء التي يصعب العثور عليها حقًا.

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

أقترح بشدة:

  1. أنت لا اجعل برنامجك متعدد الخيوط إلا إذا كنت إطلاقا بحاجة ل؛ و

  2. إذا قمت بذلك ، اشتر وقراءة من غلاف إلى تغطية براين جويتز تزامن جافا في الممارسة.

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top