جافا:إعلام() مقابلnotifyAll() في جميع أنحاء مرة أخرى

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

  •  09-06-2019
  •  | 
  •  

سؤال

إذا كان أحد غوغل عن "الفرق بين notify() و notifyAll()"ثم الكثير من التفسيرات سوف يطفو على السطح (ترك بصرف النظر عن جافادوك الفقرات).كل ذلك يتلخص إلى عدد من المواضيع انتظار يتم ايقاظي:واحد في notify() وكل ما في notifyAll().

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

ما مفيدة الفرق بين إعلام() و notifyAll() بعد ذلك ؟ أنا في عداد المفقودين شيئا ؟

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

المحلول

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

ليس هذا هو الصحيح. o.notifyAll() يستيقظ كل من المواضيع التي يتم حظرها في o.wait() المكالمات.المواضيع ويسمح فقط للعودة من o.wait() واحد من جانب واحد ، لكن كل سوف على دورهم.


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

في بعض الحالات, في انتظار كل ما يمكن أن تأخذ المواضيع المفيدة العمل مرة واحدة الانتظار التشطيبات.على سبيل المثال سيكون مجموعة من المواضيع في انتظار مهمة معينة على الانتهاء ؛ بمجرد أن المهمة قد انتهت كل المواضيع انتظار مواصلة أعمالهم.في هذه الحالة يمكنك استخدام notifyAll() أن تستيقظ كل المواضيع انتظار في نفس الوقت.

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


في كثير من الحالات ، رمز انتظار شرط يكون مكتوب حلقة:

synchronized(o) {
    while (! IsConditionTrue()) {
        o.wait();
    }
    DoSomethingThatOnlyMakesSenseWhenConditionIsTrue_and_MaybeMakeConditionFalseAgain();
}

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

نصائح أخرى

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

قراءة بعناية وفهم.الرجاء ارسال لي رسالة عبر البريد الإلكتروني إذا كان لديك أي أسئلة.

نظرة على المنتج/المستهلك (الافتراض هو املنتج-املستهلك الدرجة مع اثنين من الأساليب).فمن كسر (لأنه يستخدم notify) نعم قد تعمل - حتى أكثر من مرة ، ولكن قد أيضا أن يسبب الجمود - سوف نرى لماذا:

public synchronized void put(Object o) {
    while (buf.size()==MAX_SIZE) {
        wait(); // called if the buffer is full (try/catch removed for brevity)
    }
    buf.add(o);
    notify(); // called in case there are any getters or putters waiting
}

public synchronized Object get() {
    // Y: this is where C2 tries to acquire the lock (i.e. at the beginning of the method)
    while (buf.size()==0) {
        wait(); // called if the buffer is empty (try/catch removed for brevity)
        // X: this is where C1 tries to re-acquire the lock (see below)
    }
    Object o = buf.remove(0);
    notify(); // called if there are any getters or putters waiting
    return o;
}

أولا ،

لماذا نحتاج إلى حلقة في حين المحيطة الانتظار ؟

نحن بحاجة إلى while حلقة في هذه الحالة:

المستهلك 1 (C1) أدخل متزامنة كتلة عازلة فارغة لذلك C1 هو وضع الانتظار مجموعة (عبر wait المكالمة).المستهلك 2 (C2) هي على وشك الدخول إلى مزامنة الطريقة (في النقطة Y أعلاه) ، ولكن منتج P1 يضع كائن في المخزن المؤقت ثم يدعو notify.فقط في انتظار موضوع C1, لذلك فمن استيقظ و الآن يحاول إعادة اكتساب الكائن قفل عند النقطة X (أعلاه).

الآن C1 و C2 يحاولون الحصول على التزامن قفل.واحد منهم (nondeterministically) هو المختار ويدخل طريقة أخرى يتم حظر (لا تنتظر - ولكن المحظورة ، في محاولة للحصول على قفل على طريقة).دعونا نقول C2 يحصل القفل الأول.C1 هو لا يزال حظر (في محاولة للحصول على قفل في العاشر).C2 يكمل طريقة و تطلق قفل.الآن, C1 يكتسب القفل.تخمين ما ، الحظ لدينا while حلقة ، لأن C1 ينفذ حلقة الاختيار (حارس) و هو منعه من إزالة غير موجودة عنصر من المخزن المؤقت (C2 حصلت عليه بالفعل!).إذا لم يكن لديك while, نحن سوف تحصل على IndexArrayOutOfBoundsException كما C1 يحاول إزالة العنصر الأول من المخزن المؤقت!

الآن ،

حسنا, الآن لماذا نحن بحاجة إلى notifyAll?

في المنتج/المستهلك المثال أعلاه يبدو أننا يمكن أن تحصل بعيدا مع notify.يبدو هذا الطريق لأننا يمكن أن يثبت أن الحراس على انتظر حلقات المنتج والمستهلك بعضها بعضا.أي أنه يبدو أننا لا يمكن أن يكون موضوع الانتظار في put طريقة وكذلك get طريقة لأن هذا أن يكون صحيحا ، فإنه يجب أن يكون صحيحا:

buf.size() == 0 AND buf.size() == MAX_SIZE (نفترض COUNT 0)

ومع ذلك, هذا ليس جيدا بما فيه الكفاية ، نحن بحاجة إلى استخدام notifyAll.دعونا نرى لماذا....

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

الخطوة 1:
- P1 يضع 1 شار في المخزن المؤقت

الخطوة 2:
- P2 محاولات put - الشيكات انتظر حلقة بالفعل شار - ينتظر

الخطوة 3:
- P3 محاولات put - الشيكات انتظر حلقة بالفعل شار - ينتظر

الخطوة 4:
- C1 محاولات الحصول على 1 شار
- C2 محاولات الحصول على 1 شار الكتل على الدخول إلى get طريقة
- C3 محاولات الحصول على 1 شار الكتل على الدخول إلى get طريقة

الخطوة 5:
- C1 هو تنفيذ get الطريقة - يحصل على شار المكالمات notify, والمخارج طريقة
ـ notify يستيقظ P2
- ولكن, C2 يدخل الأسلوب قبل P2 يمكن (P2 يجب أن نعيد القفل) ، لذلك P2 الكتل على الدخول إلى put طريقة
- C2 الشيكات انتظر حلقة لا أكثر حرف في المخزن المؤقت ، لذلك ينتظر
- C3 يدخل الأسلوب بعد C2, ولكن قبل P2, الشيكات انتظر حلقة لا أكثر حرف في المخزن المؤقت ، لذلك ينتظر

الخطوة 6:
- الآن:هناك P3, C2 و C3 في انتظار!
- أخيرا P2 يكتسب قفل يضع شار في المخزن المؤقت, المكالمات يخطر والمخارج طريقة

الخطوة 7:
- P2 إعلام يستيقظ P3 (تذكر أي موضوع يمكن أن استيقظ)
- P3 يتحقق انتظر حلقة الشرط ، هناك بالفعل شار في المخزن المؤقت ، لذلك ينتظر.
- لا مزيد من المواضيع الاتصال يخطر وثلاثة المواضيع بشكل دائم مع وقف التنفيذ!

الحل:محل notify مع notifyAll في المنتج/المستهلك رمز (أعلاه).

مفيدة الاختلافات:

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

  • استخدام notifyAll() في الحالات الأخرى حيث المواضيع انتظار قد يكون لها أغراض مختلفة وينبغي أن تكون قادرة على تشغيل بشكل متزامن.مثال على ذلك هو عملية الصيانة على مورد مشترك فيها مواضيع متعددة ينتظرون عملية كاملة قبل الوصول إلى الموارد.

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

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

وجدت التفسير الكبير هنا.باختصار:

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

مع ملاحظة أن التزامن المرافق لديك أيضا خيار بين signal() و signalAll() مثل هذه الأساليب تسمى هناك.لذا فإن السؤال لا يزال ساري المفعول حتى مع java.util.concurrent.

دوغ ليا يجلب نقطة مثيرة للاهتمام في الكتاب الشهير:إذا notify() و Thread.interrupt() يحدث في نفس الوقت يخطر قد تضيع.إذا كان هذا يمكن أن يحدث عواقب وخيمة notifyAll() هو خيار أكثر أمانا حتى ولو كنت تدفع الثمن من النفقات العامة (الاستيقاظ الكثير من المواضيع أكثر من مرة).

من جوشوا بلوخ, جافا المعلم نفسه في فعالية جافا 2 الإصدار:

"البند 69:تفضل التزامن المرافق الانتظار إخطار".

هنا هو مثال على ذلك.تشغيله.ثم تغيير أحد notifyAll() إخطار() ونرى ما سيحدث.

ProducerConsumerExample الدرجة

public class ProducerConsumerExample {

    private static boolean Even = true;
    private static boolean Odd = false;

    public static void main(String[] args) {
        Dropbox dropbox = new Dropbox();
        (new Thread(new Consumer(Even, dropbox))).start();
        (new Thread(new Consumer(Odd, dropbox))).start();
        (new Thread(new Producer(dropbox))).start();
    }
}

دروببوإكس الدرجة

public class Dropbox {

    private int number;
    private boolean empty = true;
    private boolean evenNumber = false;

    public synchronized int take(final boolean even) {
        while (empty || evenNumber != even) {
            try {
                System.out.format("%s is waiting ... %n", even ? "Even" : "Odd");
                wait();
            } catch (InterruptedException e) { }
        }
        System.out.format("%s took %d.%n", even ? "Even" : "Odd", number);
        empty = true;
        notifyAll();

        return number;
    }

    public synchronized void put(int number) {
        while (!empty) {
            try {
                System.out.println("Producer is waiting ...");
                wait();
            } catch (InterruptedException e) { }
        }
        this.number = number;
        evenNumber = number % 2 == 0;
        System.out.format("Producer put %d.%n", number);
        empty = false;
        notifyAll();
    }
}

الطبقة الاستهلاكية

import java.util.Random;

public class Consumer implements Runnable {

    private final Dropbox dropbox;
    private final boolean even;

    public Consumer(boolean even, Dropbox dropbox) {
        this.even = even;
        this.dropbox = dropbox;
    }

    public void run() {
        Random random = new Random();
        while (true) {
            dropbox.take(even);
            try {
                Thread.sleep(random.nextInt(100));
            } catch (InterruptedException e) { }
        }
    }
}

المنتج الفئة

import java.util.Random;

public class Producer implements Runnable {

    private Dropbox dropbox;

    public Producer(Dropbox dropbox) {
        this.dropbox = dropbox;
    }

    public void run() {
        Random random = new Random();
        while (true) {
            int number = random.nextInt(10);
            try {
                Thread.sleep(random.nextInt(100));
                dropbox.put(number);
            } catch (InterruptedException e) { }
        }
    }
}

ملخص قصير:

دائما تفضل notifyAll() أكثر إعلام() إلا إذا كان لديك نطاق واسع التطبيق الموازي حيث يوجد عدد كبير من المواضيع في جميع تفعل الشيء نفسه.

التفسير:

إعلام() [...] يستيقظ واحد الخيط.لأن إعلام() لا يسمح لك لتحديد الخيط الذي هو استيقظ هو مفيد فقط في نطاق واسع من تطبيقات موازية — أن هي البرامج مع عدد كبير من المواضيع ، كل فعل مماثلة الأعمال.في مثل هذا الطلب لا تهتم والتي الموضوع استيقظ.

المصدر: https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

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

وهكذا ، إذا لم يكن لديك التطبيق حيث عدد كبير من المواضيع تفعل الشيء نفسه في وقت واحد ، تفضل notifyAll() أكثر إعلام().لماذا ؟ لأن المستخدمين الآخرين قد أجبت في هذا المنتدى ، إعلام()

يستيقظ موضوع واحد وهذا هو ينتظر هذا الكائن مراقبة.[ ... ] ، الخيار التعسفي ويحدث في تقدير التنفيذ.

المصدر:جافا SE8 API (https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify--)

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

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

أنا مندهش جدا أن لا أحد ذكر الشائنة "خسر التنبيه" مشكلة (جوجل).

في الأساس:

  1. إذا كان لديك العديد من المواضيع في انتظار على نفس الشرط ،
  2. متعددة مؤشرات الترابط التي يمكن أن تجعلك الانتقال من الدولة إلى الدولة باء ،
  3. متعددة مؤشرات الترابط التي يمكن أن تجعلك الانتقال من الدولة ب أن الدولة (عادة نفس المواضيع كما في 1.) ،
  4. الانتقال من أ إلى ب أن يخطر المواضيع في 1.

ثم يجب عليك استخدام notifyAll إلا إذا كان لديك يمكن اثباتها الضمانات التي فقدت wakeups مستحيلة.

مثال شائع هو المتزامنة FIFO الانتظار حيث:متعددة enqueuers (1.و 3.أعلاه) يمكن الانتقال قائمة الانتظار الخاصة بك فارغة من غير فارغة متعددة dequeuers (2.أعلاه) يمكن أن تنتظر حالة "الانتظار ليست فارغة" فارغ -> غير فارغة يجب أن يخطر dequeuers

يمكنك بسهولة كتابة التداخل من العمليات التي تبدأ من فارغة الانتظار ، 2 enqueuers و 2 dequeuers التفاعل و 1 enqueuer ستبقى النوم.

هذه هي مشكلة يمكن القول للمقارنة مع الجمود المشكلة.

آمل أن يكون هذا سوف واضحة بعض الشكوك.

إعلام() :إعلام() طريقة يستيقظ موضوع واحد في انتظار للقفل (أول الخيط الذي يسمى الانتظار() على أن قفل).

notifyAll() :على notifyAll() طريقة يستيقظ كل المواضيع في انتظار القفل ، JVM يختار واحد من المواضيع من قائمة المواضيع في انتظار قفل يستيقظ هذا الموضوع حتى.

في حالة وجود موضوع واحد في انتظار قفل, لا يوجد فرق كبير بين إعلام() و notifyAll().ومع ذلك ، عندما يكون هناك أكثر من موضوع واحد في انتظار قفل في كل من يخطر() و notifyAll () نفس الموضوع استيقظ هو تحت سيطرة JVM و لا يمكنك برمجيا التحكم الاستيقاظ موضوع معين.

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

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

notify() سوف يستيقظ موضوع واحد حين notifyAll() سوف يستيقظ كل شيء.بقدر ما أعرف لا يوجد وسط.ولكن إذا كنت غير متأكد ما notify() لا المواضيع الخاصة بك, استخدام notifyAll().تعمل مثل السحر في كل مرة.

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

هنا هو أبسط تفسير:

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

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

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

هناك ثلاث دول عن الموضوع.

  1. انتظر الموضوع هو عدم استخدام أي دورة وحدة المعالجة المركزية
  2. منعت - في موضوع محظور في محاولة للحصول على الشاشة.قد لا يزال يتم استخدام CPU
  3. تشغيل مؤشر الترابط قيد التشغيل.

الآن عندما يخطر() ويسمى ، JVM يختار موضوع واحد ونقلها إلى أن منعت الدولة ومن ثم إلى إدارة الدولة حيث لا توجد منافسة من أجل رصد الكائن.

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

هذا الجواب هو الرسومية كتابة و تبسيط ممتازة الإجابة من قبل xagyg, ، بما في ذلك تعليقات عيران.

لماذا استخدام notifyAll ، حتى عندما يكون كل منتج هو المقصود واحد المستهلك ؟

تنظر المنتجين والمستهلكين مبسط على النحو التالي.

المنتج:

while (!empty) {
   wait() // on full
}
put()
notify()

المستهلك:

while (empty) {
   wait() // on empty
}
take()
notify()

نفترض 2 المنتجين و 2 المستهلكين, تبادل العازلة من حجم 1.الصورة التالية يصور السيناريو مما يؤدي إلى الجمود, التي يمكن تجنبها لو كل المواضيع المستخدمة notifyAll.

كل تخطر هو المسمى مع الخيط أن استيقظ.

deadlock due to notify

notify() يتيح لك كتابة رمز أكثر كفاءة من notifyAll().

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

synchronized(this) {
    while(busy) // a loop is necessary here
        wait();
    busy = true;
}
...
synchronized(this) {
    busy = false;
    notifyAll();
}

أنها يمكن أن تكون أكثر كفاءة باستخدام notify():

synchronized(this) {
    if(busy)   // replaced the loop with a condition which is evaluated only once
        wait();
    busy = true;
}
...
synchronized(this) {
    busy = false;
    notify();
}

في حالة إذا كان لديك عدد كبير من المواضيع ، أو إذا انتظر حلقة الشرط مكلفة تقييم ، notify() سوف تكون أسرع بكثير من notifyAll().على سبيل المثال, إذا كان لديك 1000 المواضيع ثم 999 المواضيع سوف يكون استيقظ و تقييمها بعد أول notifyAll(), ثم 998 ، ثم 997 ، وهلم جرا.على العكس من ذلك ، notify() الحل مؤشر واحد فقط سوف يكون استيقظ.

استخدام notifyAll() عندما تحتاج إلى اختيار أي موضوع سيتم القيام بهذا العمل التالي:

synchronized(this) {
    while(idx != last+1)  // wait until it's my turn
        wait();
}
...
synchronized(this) {
    last = idx;
    notifyAll();
}

وأخيرا ، من المهم أن نفهم أنه في حالة notifyAll(), مدونة داخل synchronized القطع التي تم أيقظ سيتم تنفيذه بالتتابع, ليس في كل مرة.دعنا نقول أن هناك ثلاثة مواضيع الانتظار في المثال أعلاه ، والرابع موضوع المكالمات notifyAll().جميع المواضيع الثلاثة سوف يكون استيقظ ولكن واحدة فقط سوف تبدأ التنفيذ والتحقق من حالة من while حلقة.إذا كان الشرط هو true, ، فإنه سيتم استدعاء wait() مرة أخرى, وبعد ذلك فقط الموضوع الثاني سوف تبدأ تنفيذ و سوف تحقق while حلقة الشرط ، وهلم جرا.

مأخوذة من بلوق على فعالية جافا:

The notifyAll method should generally be used in preference to notify. 

If notify is used, great care must be taken to ensure liveness.

لذا ما أفهمه هو (من المذكور بلوق, التعليق من قبل "يان TM" على الإجابة المقبولة و جافا مستندات):

  • إعلام() :JVM يوقظ أحد المواضيع انتظار على هذا الكائن.موضوع الاختيار بشكل تعسفي دون الإنصاف.حتى نفس الموضوع يمكن أن استيقظ مرة أخرى و مرة أخرى.حتى نظام الدولة التغييرات لكن لم يتم إحراز تقدم حقيقي.وبالتالي خلق livelock.
  • notifyAll() :JVM يوقظ كل المواضيع ثم كل المواضيع سباق قفل على هذا الكائن.الآن, وحدة المعالجة المركزية جدولة يختار الموضوع الذي يكتسب قفل على هذا الكائن.عملية الاختيار هذه أن يكون أفضل بكثير من اختيار JVM.وبالتالي ضمان liveness.

نلقي نظرة على مدونة نشرت بواسطة @xagyg.

لنفترض اثنين من المواضيع المختلفة ينتظرون حالتين:
على أول الخيط ينتظر buf.size() != MAX_SIZE, ، الخيط الثاني ينتظر buf.size() != 0.

لنفترض أن في بعض نقطة buf.size() لا يساوي 0.JVM المكالمات notify() بدلا من notifyAll(), و أول موضوع يتم إخطار (لا ثانية واحدة).

الموضوع الأول هو استيقظ, الشيكات buf.size() الذي قد يعود MAX_SIZE, و يعود إلى انتظار.الموضوع الثاني هو استيقظ ، ما زال الانتظار ولا يدعو get().

notify() يستيقظ أول الخيط الذي يسمى wait() على نفس الكائن.

notifyAll() يستيقظ كل المواضيع التي دعت wait() على نفس الكائن.

الأولوية القصوى الموضوع سيتم تشغيل أول.

أود أن أذكر ما هو موضح في جافا التزامن في الممارسة:

النقطة الأولى ، سواء إعلام أو NotifyAll?

It will be NotifyAll, and reason is that it will save from signall hijacking.

إذا اثنين من المواضيع A و B ينتظرون على حالة مختلفة المسندات نفس حالة انتظار وإخطار يسمى ، ثم تصل إلى JVM وهو موضوع JVM يخطر.

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

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

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

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

notify() - يختار عشوائي الخيط من الانتظار مجموعة من وجوه ويضعه في BLOCKED الدولة.بقية المواضيع في الانتظار مجموعة من وجوه لا تزال في WAITING الدولة.

notifyAll() - تنقل جميع المواضيع من الانتظار مجموعة من الكائن إلى BLOCKED الدولة.بعد استخدام notifyAll(), لا توجد مواضيع المتبقية في الانتظار تعيين الكائن المشتركة لأن كل منهم الآن في BLOCKED الدولة وليس في WAITING الدولة.

BLOCKED حظر على قفل الاستحواذ.WAITING - في انتظار إبلاغ ( أو حظر من أجل الانضمام إلى الإنجاز ).

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

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

في المقابل ، notifyAll() تمكن جميع المواضيع انتظار في نهاية المطاف إلى إعادة اكتساب قفل و تحقق كل منهما الشرط ، مما يؤدي في نهاية المطاف مما تقدم.

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

عند استدعاء الانتظار () "موضوع"(أتوقع الكائن قفل المكتسبة) ، المتدرب هذا سوف الافراج عن قفل على موضوع المساعدة على المواضيع الأخرى أن يكون قفل على هذا "الكائن" في هذا السيناريو سوف يكون هناك أكثر من 1 الموضوع في انتظار "الموارد/كائن"(النظر في المواضيع الأخرى كما أصدر الانتظار على نفس فوق كائن أسفل الطريق سوف يكون هناك خيط تعبئة الموارد/موضوع استدعاء تخطر/notifyAll).

هنا عند إصدار إخطار من نفس الكائن(من نفس/الجانب الآخر من عملية/الرمز),هذا سوف الافراج عن حظر انتظار ترابط واحد (ليس كل المواضيع انتظار -- صدر هذا الموضوع سوف تكون التقطت من قبل JVM موضوع جدولة جميع قفل الحصول على الكائن نفسه العادية).

إذا كان لديك مؤشر واحد فقط من شأنها أن تكون مشاركة/العمل على هذا الكائن هو موافق لاستخدام يخطر() طريقة وحده في الانتظار-إعلام التنفيذ.

إذا كنت في حالة حيث أكثر من موضوع واحد يقرأ و يكتب على الموارد/الكائن على أساس منطق تسلسل العمل الخاص بك,ثم يجب عليك الذهاب notifyAll()

الآن أنا أبحث بالضبط كيف jvm هو تحديد وكسر انتظار الموضوع عندما نصدر يخطر() على وجوه ...

في حين أن هناك بعض خالص الإجابات أعلاه, أنا مندهش من قبل عدد من الالتباس وسوء الفهم قرأت.وهذا ربما يثبت فكرة أن واحد يجب أن تستخدم جافا.util.المتزامنة قدر الإمكان بدلا من محاولة الكتابة الخاصة مكسورة المتزامنة رمز.نعود إلى السؤال:لتلخيص ، أفضل الممارسات اليوم هو تجنب يخطر() في جميع الحالات بسبب خسر التنبيه المشكلة.أي شخص لا يفهم هذا لا ينبغي أن يسمح له بكتابة مهمة التزامن رمز.إذا كنت قلقا بشأن الرعي مشكلة واحدة آمنة وسيلة لتحقيق اليقظة موضوع واحد في كل مرة هو:1.بناء صريح في انتظار انتظار انتظار المواضيع ؛ 2.يكون كل موضوع في طابور الانتظار على انها السلف;3.يكون كل موضوع الدعوة notifyAll() عند القيام به.أو يمكنك استخدام جافا.util.المتزامنة.*, التي نفذت بالفعل هذا.

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

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