Apakah wake up palsu di Jawa benar-benar terjadi?

208

Melihat berbagai pertanyaan terkait penguncian dan (hampir) selalu menemukan 'loop karena istilah wakeups palsu' 1 Saya bertanya-tanya, adakah yang pernah mengalami wakeup semacam itu (misalnya, dengan asumsi lingkungan hardware / software yang layak)?

Saya tahu istilah 'palsu' berarti tidak ada alasan yang jelas tetapi apa yang bisa menjadi alasan untuk acara semacam itu?

( 1 Catatan: Saya tidak mempertanyakan praktik perulangan.)

Sunting: Pertanyaan pembantu (bagi mereka yang suka contoh kode):

Jika saya memiliki program berikut, dan saya menjalankannya:

public class Spurious {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition cond = lock.newCondition();
        lock.lock();
        try {
            try {
                cond.await();
                System.out.println("Spurious wakeup!");
            } catch (InterruptedException ex) {
                System.out.println("Just a regular interrupt.");
            }
        } finally {
            lock.unlock();
        }
    }
}

Apa yang bisa saya lakukan untuk membangunkan ini awaitdengan palsu tanpa menunggu selamanya untuk acara acak?

akarnokd
sumber
1
Untuk JVM yang berjalan pada sistem POSIX dan menggunakan pthread_cond_wait()pertanyaan sebenarnya adalah "Mengapa pthread_cond_wait memiliki wakeups palsu?" .
Aliran

Jawaban:

204

Artikel Wikipedia tentang wake up palsu memiliki berita gembira ini:

The pthread_cond_wait()fungsi dalam Linux diimplementasikan dengan menggunakan futexsystem call. Setiap panggilan sistem pemblokiran di Linux kembali dengan tiba-tiba EINTRketika proses menerima sinyal. ... pthread_cond_wait()tidak dapat memulai kembali menunggu karena mungkin akan melewatkan bangun yang nyata dalam waktu sedikit di luar futexpanggilan sistem. Kondisi balapan ini hanya dapat dihindari oleh penelepon yang memeriksa invarian. Sinyal POSIX karenanya akan menghasilkan wake up yang palsu.

Rangkuman : Jika ada proses Linux yang diberi tanda, masing-masing utasnya akan menikmati bangun yang bagus dan panas .

Saya membelinya. Itu adalah pil yang lebih mudah untuk menelan daripada alasan "itu untuk kinerja" yang sering diberikan.

John Kugelman
sumber
13
Penjelasan yang lebih baik di sini: stackoverflow.com/questions/1461913/…
Gili
3
Pemblokiran EINTR ini berlaku untuk semua pemblokiran panggilan sistem di sistem turunan Unix. Ini membuat kernel jauh lebih mudah, tetapi programmer aplikasi menanggung beban.
Tim Williscroft
2
Saya pikir pthread_cond_wait () dan teman-teman tidak dapat mengembalikan EINTR, tetapi mengembalikan nol jika dibangunkan secara palsu? Dari: pubs.opengroup.org/onlinepubs/7908799/xsh/… "Fungsi-fungsi ini tidak akan mengembalikan kode kesalahan [EINTR]."
gubby
2
@ jgubby Itu benar. futex()Panggilan yang mendasarinya kembali EINTR, tetapi nilai kembali itu tidak menggelembung ke tingkat berikutnya. Penelepon pthread karena itu harus memeriksa invarian. Apa yang mereka katakan adalah bahwa ketika pthread_cond_wait()kembali, Anda harus memeriksa kembali kondisi loop Anda (invarian), karena penantian mungkin telah dibangunkan secara palsu. Menerima sinyal selama panggilan sistem adalah salah satu penyebab yang mungkin, tetapi itu bukan satu-satunya.
John Kugelman
1
Agaknya, pthreadperpustakaan bisa menyediakan invarian sendiri dan logika pemeriksaan sendiri untuk menghilangkan bangun palsu, daripada menyerahkan tanggung jawab itu kepada pengguna. Itu akan (mungkin) memiliki dampak kinerja yang diklaim.
22

Saya memiliki sistem produksi yang menunjukkan perilaku ini. Utas menunggu sinyal bahwa ada pesan dalam antrian. Dalam periode sibuk, hingga 20% dari bangun adalah palsu (yaitu ketika bangun tidak ada dalam antrian). Utas ini adalah satu-satunya konsumen pesan. Ini berjalan pada kotak Linux SLES-10 8-prosesor dan dibangun dengan GCC 4.1.2. Pesan berasal dari sumber eksternal dan diproses secara tidak sinkron karena ada masalah jika sistem saya tidak membacanya dengan cukup cepat.

Mr.Dirty.Birdy
sumber
15

Untuk menjawab pertanyaan dalam titile - Ya! itu memang terjadi. Meskipun artikel Wiki menyebutkan banyak tentang wakeups palsu penjelasan yang bagus untuk hal yang sama yang saya temui adalah sebagai berikut -

Bayangkan saja ... seperti kode apa pun, penjadwal thread dapat mengalami pemadaman sementara karena sesuatu yang tidak normal terjadi pada perangkat keras / lunak yang mendasarinya. Tentu saja, kehati-hatian harus dilakukan agar hal ini terjadi sesedikit mungkin, tetapi karena tidak ada perangkat lunak yang kuat 100%, masuk akal untuk menganggap ini dapat terjadi dan berhati-hati pada pemulihan yang anggun jika seandainya scheduler mendeteksi ini (mis. dengan mengamati detak jantung yang hilang).

Sekarang, bagaimana scheduler dapat pulih, dengan mempertimbangkan bahwa selama pemadaman itu bisa kehilangan beberapa sinyal yang dimaksudkan untuk memberi tahu utas menunggu? Jika scheduler tidak melakukan apa pun, utas yang disebutkan "tidak beruntung" hanya akan hang, menunggu selamanya - untuk menghindari ini, scheduler hanya akan mengirim sinyal ke semua utas yang menunggu.

Ini membuatnya perlu untuk membuat "kontrak" bahwa utas menunggu dapat diberitahukan tanpa alasan. Tepatnya, akan ada alasan - pemadaman penjadwal - tetapi karena utas dirancang (untuk alasan yang baik) untuk tidak mengetahui detail implementasi internal penjadwal, alasan ini kemungkinan lebih baik untuk ditampilkan sebagai "palsu".

Saya membaca jawaban ini dari Sumber dan merasa cukup masuk akal. Baca juga

Kebangkitan palsu di Jawa dan cara menghindarinya .

PS: Tautan di atas adalah ke blog pribadi saya yang memiliki detail tambahan tentang bangun palsu.

Aniket Thakur
sumber
9

Cameron Purdy menulis posting blog beberapa waktu lalu tentang terkena masalah wakeup palsu. Jadi ya, itu terjadi

Saya menduga itu dalam spesifikasi (karena kemungkinan) karena keterbatasan beberapa platform yang digunakan Java? meskipun saya mungkin salah!

oxbow_lakes
sumber
Saya membaca posting dan memberi saya ide tentang memiliki unit test untuk menguji kesesuaian satu aplikasi dengan paradigma looping-wait dengan membangunkannya secara acak / deterministik. Atau sudah tersedia di suatu tempat?
akarnokd
Ini pertanyaan lain di SO: "Apakah ada VM ketat yang dapat digunakan untuk pengujian?". Saya ingin melihat satu dengan memori thread-lokal yang ketat - Saya belum berpikir mereka ada
oxbow_lakes
8

Tambahkan saja ini. Ya itu terjadi dan saya menghabiskan tiga hari mencari penyebab masalah multi-threading pada mesin 24 core (JDK 6). 4 dari 10 eksekusi mengalami hal itu tanpa pola apa pun. Ini tidak pernah terjadi pada 2 inti atau 8 core.

Mempelajari beberapa materi online dan ini bukan masalah Java tetapi perilaku umum yang jarang tetapi diharapkan.

Rene
sumber
Halo ReneS, apakah Anda mengembangkan aplikasi yang sedang berjalan di sana? Apakah (apakah) ia menunggu () metode memanggil sementara loop memeriksa kondisi eksternal seperti yang disarankan dalam java doc docs.oracle.com/javase/6/docs/api/java/lang/… ?
Gumkins
Saya menulis tentang hal itu dan ya solusinya adalah loop sementara dengan kondisi cek. Kesalahan saya adalah loop yang hilang ... tapi saya belajar tentang wakeup ini ... tidak pernah menggunakan dua core, sering di 24cores blog.xceptance.com/2011/05/06/spurious-wakeup-the-rare-event
ReneS
Saya memiliki pengalaman serupa ketika saya menjalankan aplikasi pada server 40+ core unix. Itu memiliki jumlah ekstrim dari bangun palsu. - Jadi, sepertinya jumlah wake yang palsu berbanding lurus dengan jumlah core prosesor sistem.
bvdb
0

https://stackoverflow.com/a/1461956/14731 berisi penjelasan yang sangat baik tentang mengapa Anda perlu berjaga-jaga terhadap bangun palsu bahkan jika sistem operasi yang mendasarinya tidak memicu mereka. Sangat menarik untuk dicatat bahwa penjelasan ini berlaku di berbagai bahasa pemrograman, termasuk Java.

Gili
sumber
0

Menjawab pertanyaan OP

Apa yang bisa saya lakukan untuk membangunkan ini, menunggu dengan palsu tanpa menunggu selamanya untuk acara acak?

, tidak ada bangun palsu yang dapat membangunkan utas menunggu ini!

Terlepas dari apakah wakeups palsu dapat atau tidak bisa terjadi pada platform tertentu, dalam kasus potongan OP itu tidak mungkin untukCondition.await() kembali dan melihat garis "Wake up palsu!" dalam aliran output.

Kecuali Anda menggunakan Java Class Library yang sangat eksotis

Hal ini karena standar, OpenJDK 's ReentrantLock' s metode newCondition()pengembalian yang AbstractQueuedSynchronizer'implementasi s dari Conditioninterface, bersarang ConditionObject(by the way, itu adalah satu-satunya implementasi Conditionantarmuka di perpustakaan kelas ini), dan ConditionObject' s metode await()itu sendiri cek apakah kondisi tidak memegang dan tidak ada bangun palsu dapat memaksa metode ini untuk kembali secara keliru.

Omong-omong, Anda bisa memeriksanya sendiri karena cukup mudah untuk meniru bangun palsu setelah AbstractQueuedSynchronizerimplementasi berbasis-terlibat. AbstractQueuedSynchronizermenggunakan tingkat rendah LockSupport's parkdan unparkmetode, dan jika Anda menjalankan LockSupport.unparkpada thread menunggu diCondition , tindakan ini tidak dapat dibedakan dari wakeup palsu.

Sedikit refactoring cuplikan OP,

public class Spurious {

    private static class AwaitingThread extends Thread {

        @Override
        public void run() {
            Lock lock = new ReentrantLock();
            Condition cond = lock.newCondition();
            lock.lock();
            try {
                try {
                    cond.await();
                    System.out.println("Spurious wakeup!");
                } catch (InterruptedException ex) {
                    System.out.println("Just a regular interrupt.");
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private static final int AMOUNT_OF_SPURIOUS_WAKEUPS = 10;

    public static void main(String[] args) throws InterruptedException {
        Thread awaitingThread = new AwaitingThread();
        awaitingThread.start();
        Thread.sleep(10000);
        for(int i =0 ; i < AMOUNT_OF_SPURIOUS_WAKEUPS; i++)
            LockSupport.unpark(awaitingThread);
        Thread.sleep(10000);
        if (awaitingThread.isAlive())
            System.out.println("Even after " + AMOUNT_OF_SPURIOUS_WAKEUPS + " \"spurious wakeups\" the Condition is stil awaiting");
        else
            System.out.println("You are using very unusual implementation of java.util.concurrent.locks.Condition");
    }
}

, dan tidak peduli seberapa keras utas yang tidak parkir (utama) akan mencoba untuk membangunkan utas yang menunggu, Condition.await()metode ini tidak akan pernah kembali dalam kasus ini.

Wakeups palsu pada Conditionmetode yang menunggu dibahas dalam javadoc Conditionantarmuka . Meskipun dikatakan bahwa,

ketika menunggu suatu Kondisi, bangun palsu diizinkan untuk terjadi

dan itu

direkomendasikan bahwa pemrogram aplikasi selalu berasumsi bahwa mereka dapat terjadi dan selalu menunggu dalam satu lingkaran.

tetapi kemudian menambahkan itu

Suatu implementasi gratis untuk menghilangkan kemungkinan wake up palsu

dan AbstractQueuedSynchronizerimplementasi Conditionantarmuka tidak persis seperti itu - menghilangkan segala kemungkinan wakeup palsu .

Ini tentu berlaku untuk ConditionObjectmetode lain yang menunggu.

Jadi kesimpulannya adalah:

kita harus selalu memanggil Condition.awaitloop dan memeriksa apakah kondisinya tidak berlaku, tetapi dengan standar, OpenJDK, Java Class Library tidak pernah bisa terjadi . Kecuali, sekali lagi, Anda menggunakan Java Class Library yang sangat tidak biasa (yang pasti sangat sangat tidak biasa, karena Java Class Library non-OpenJDK lain yang terkenal, yang saat ini hampir punah GNU Classpath dan Apache Harmony , tampaknya memiliki identik dengan implementasi standar Conditionantarmuka)

igor.zh
sumber