Java'daki sahte uyandırma işlemleri gerçekten oluyor mu?


208

Kilitleme ile ilgili çeşitli soruları görmek ve (neredeyse) her zaman ' uyandırma modları nedeniyle' döngüyü bulmak 'terimleri 1 Acaba, kimse böyle bir uyanma yaşadı mı (örneğin iyi bir donanım / yazılım ortamı varsayarak)?

'Sahte' teriminin belirgin bir neden olmadığını biliyorum ama böyle bir olayın nedenleri neler olabilir?

( 1 Not: Döngü uygulamasını sorgulamıyorum.)

Düzenleme: Yardımcı bir soru (kod örneklerini sevenler için):

Aşağıdaki programım varsa ve çalıştırırsam:

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();
        }
    }
}

awaitSonsuza kadar rastgele bir olay beklemeden bunu uyandırmak için ne yapabilirim ?


1
POSIX sistemlerinde çalışan ve pthread_cond_wait()gerçek soruyu kullanan JVM'ler için "Neden pthread_cond_wait'te sahte uyandırma var?" .
Akış

Yanıtlar:


204

Sahte uyandırma makaleleri hakkındaki Wikipedia makalesinde şu tidbit var:

pthread_cond_wait()Linux fonksiyon kullanılarak uygulanır futexsistem çağrısı. Linux üzerindeki her engelleme sistemi çağrısı EINTR, işlem bir sinyal aldığında aniden geri döner . ... pthread_cond_wait()beklemesini yeniden başlatamaz çünkü futexsistem çağrısının dışındayken gerçek bir uyanmayı kaçırabilir . Bu yarış koşulundan yalnızca arayan bir değişmez olup olmadığını kontrol edebilir. POSIX sinyali bu nedenle sahte bir uyandırma oluşturur.

Özet : Bir Linux işlemi belirtilirse, bekleyen iş parçacıklarının her biri hoş, sıcak ve sahte bir uyanmanın keyfini çıkarır .

Ben satın alıyorum. Yutmak daha sık verilen tipik "belirsiz performans" nedeninden daha kolay bir haptır.


13
Burada daha iyi bir açıklama: stackoverflow.com/questions/1461913/…
Gili

3
Bu EINTR engellemeyi kaldırma, Unix'ten türetilen sistemlerde tüm engelleme sistemi çağrıları için geçerlidir. Bu, çekirdek lotlarını basitleştirdi, ancak uygulama programcıları yükü satın aldı.
Tim Williscroft

2
Ben pthread_cond_wait () ve arkadaşlar EINTR döndürmek değil, ama uyandı eğer uyandım sıfır döndü düşündüm? Gönderen: pubs.opengroup.org/onlinepubs/7908799/xsh/… "Bu işlevler [EINTR] hata kodunu döndürmez."
gubby

2
@jgubby Doğru. Temel futex()çağrı geri döner EINTR, ancak bu dönüş değeri bir sonraki seviyeye yükseltilmez. Bu nedenle pthread arayan bir değişmez olup olmadığını kontrol etmelidir. Söyledikleri şey, pthread_cond_wait()geri döndüğünde döngü durumunuzu (değişmez) tekrar kontrol etmeniz gerektiğidir, çünkü bekleme sahte bir şekilde uyandırılmış olabilir. Sistem çağrısı sırasında sinyal almak olası bir nedendir, ancak tek değildir.
John Kugelman

1
Muhtemelen, pthreadkütüphane bu sorumluluğu kullanıcıya aktarmak yerine sahte uyanmaları ortadan kaldırmak için kendi değişmezini ve kendi kontrol mantığını sağlayabilir. Bunun (muhtemelen) iddia edilen performans etkisi olacaktır.

22

Bu davranışı sergileyen bir üretim sistemim var. Bir iş parçacığı, kuyrukta bir ileti olduğunu gösteren bir sinyal bekler. Yoğun dönemlerde, uyanmaların% 20'sine kadar sahte (yani uyandığında kuyrukta hiçbir şey yoktur). Bu ileti dizisi iletilerin tek tüketicisidir. Bir Linux SLES-10 8 işlemci kutusuyla çalışır ve GCC 4.1.2 ile oluşturulmuştur. İletiler harici bir kaynaktan geliyor ve sistemim bunları yeterince hızlı okumazsa sorunlar olduğu için eşzamansız olarak işleniyor.


15

Sorudaki soruyu cevaplamak için - Evet! Wiki makalesi , sahte uyandırmalarla ilgili iyi bir şeyden bahsetmesine rağmen, karşılaştığım şey için güzel bir açıklama aşağıdaki gibidir -

Sadece düşünün ... herhangi bir kod gibi, iş parçacığı zamanlayıcısı, altta yatan donanım / yazılımda anormal bir şey nedeniyle geçici karartma yaşayabilir. Tabii ki, bunun olabildiğince nadir olmasına dikkat edilmelidir, ancak% 100 sağlam yazılım diye bir şey olmadığından, bunun olabileceğini varsaymak ve zamanlayıcı bunu tespit ederse zarif iyileşmeye dikkat etmek mantıklıdır (ör. eksik kalp atışlarını gözlemleyerek).

Şimdi, zamanlayıcı, karartma sırasında bekleyen konuları bildirmek için bazı sinyalleri kaçırabileceğini dikkate alarak nasıl kurtulabilir? Zamanlayıcı hiçbir şey yapmazsa, bahsedilen "şanssız" iş parçacıkları sonsuza kadar bekler, asılır - bundan kaçınmak için, zamanlayıcı tüm bekleyen iş parçacıklarına bir sinyal gönderir.

Bu, bekleyen evrenin bir sebep olmaksızın bildirilebileceği bir "sözleşme" oluşturulmasını gerekli kılar. Kesin olmak gerekirse, bir neden - zamanlayıcı karartması - olurdu, ancak iş parçacığı zamanlayıcı iç uygulama ayrıntılarından habersiz olarak tasarlandığından (iyi bir nedenden dolayı) bu nedenin "sahte" olarak sunulması daha iyidir.

Ben bu cevabı okuyordu Kaynak ve ona makul yeterince gördük. Ayrıca okuyun

Java'da sahte uyandırma ve nasıl önlenir .

Not: Yukarıdaki bağlantı, sahte uyandırmalarla ilgili ek ayrıntılara sahip olan kişisel bloguma.


9

Cameron Purdy , sahte uyandırma probleminden vurulmak üzere bir süre önce bir blog yazısı yazdı . Yani evet, olur

Java dağıtıldığı bazı platformların sınırlamaları nedeniyle spec (olasılık olarak) tahmin ediyorum? her ne kadar yanlış olabilirim!


Gönderiyi okudum ve bir uygulamanın loop-wait paradigmasına uygunluğunu rastgele / determinist olarak uyandırarak test etmek için birim testleri yaptırmak hakkında bir fikir verdim. Yoksa zaten bir yerde mevcut mu?
akarnokd

SO hakkında başka bir soru: " Test için kullanılabilecek katı bir VM var mı?". Sıkı yerel iş parçacığı belleği olan birini görmek isterdim - Henüz var olduklarını sanmıyorum
oxbow_lakes

8

Sadece bunu eklemek için. Evet olur ve 24 çekirdekli bir makinede (JDK 6) çoklu iş parçacığı sorununun nedenini aramak için üç gün geçirdim. 10 infazdan 4'ünde herhangi bir desen yoktu. Bu hiçbir zaman 2 çekirdek veya 8 çekirdek üzerinde gerçekleşmedi.

Bazı çevrimiçi materyaller okudu ve bu bir Java sorunu değil, genel nadir ancak beklenen bir davranış.


Merhaba ReneS, orada çalışan uygulamayı geliştiriyor muydunuz? Java doc docs.oracle.com/javase/6/docs/api/java/lang/… ' da önerildiği gibi döngü durumunu kontrol ederken wait () yöntemini çağırıyor mu?
Mart'ta gumkins

Bu konuda yazdım ve evet çözüm bir durum kontrolü ile bir while döngüsü. Benim hatam eksik döngü ... ama bu uyandırma hakkında öğrendim ... asla iki çekirdek üzerinde, genellikle 24 çekirdek blog.xceptance.com/2011/05/06/spurious-wakeup-the-rare-event
ReneS

40+ çekirdek unix sunucusunda bir uygulama çalıştırdığımda benzer deneyimler yaşadım. Aşırı miktarda uyandırma servisi vardı. - Öyleyse, sahte uyandırma miktarı, sistemin işlemci çekirdeği miktarı ile doğru orantılıdır.
bvdb

0

https://stackoverflow.com/a/1461956/14731 , temel işletim sistemi onları tetiklemese bile neden sahte uyandırmalara karşı korunmanız gerektiğinin mükemmel bir açıklamasını içerir. Bu açıklamanın Java da dahil olmak üzere birden çok programlama dilinde uygulandığını belirtmek ilginçtir.


0

OP sorusunu cevaplama

Sonsuza kadar rastgele bir olay beklemeden bunu uyandırmak için ne yapabilirim?

, hiçbir sahte uyandırma bu bekleyen iplik uyanamaz!

Sahte uyandırma işlemlerinin belirli bir platformda gerçekleşip gerçekleşmeyeceğine bakılmaksızın, OP snippet'inde "Sahte uyandırma!" Satırını geri döndürmek ve görmek imkansızdır . Condition.await()çıkış akışında.

Çok egzotik Java Sınıf Kütüphanesi kullanmadığınız sürece

Bunun nedeni standardıdır OpenJDK 'ın ReentrantLock' nin yöntemi newCondition()döndürüyor AbstractQueuedSynchronizer's uygulama Conditionara yüzü, iç içeConditionObject (bu arada, sadece uygulamasıdır Conditionbu sınıf kitaplığında arayüzünün) ve ConditionObject' nin yöntemi await()koşulu yok olmadığını kendisi kontrol eder hiçbir sahte uyandırma bu yöntemi yanlışlıkla geri dönmeye zorlayamaz.

Bu arada, AbstractQueuedSynchronizertabanlı uygulama dahil edildiğinde sahte uyandırma taklit etmek oldukça kolay olduğu için kendiniz kontrol edebilirsiniz . AbstractQueuedSynchronizerdüşük seviyeli kullanır LockSupport'spark ve unparkyöntemleri ve eğer çağırmak LockSupport.unparküzerinde bekleyen bir iş parçacığı üzerinde Condition, bu eylem bir sahte uyandırma ayırt edilemez.

OP'nin snippet'ini biraz yeniden düzenleyerek,

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");
    }
}

ve park etme (ana) iş parçacığının bekleyen iş parçacığı ne kadar zor olursa olsun, Condition.await()yöntem bu durumda geri dönmeyecektir.

Bekleyen Conditionyöntemlerle ilgili sahte uyandırma işlemleri, javadoc arayüzünde tartışıldıCondition . Her ne kadar bunu söylese de,

bir Koşul üzerinde beklerken sahte bir uyanmanın gerçekleşmesine izin verilir

ve şu

uygulama programcılarının her zaman oluşabileceklerini varsaymaları ve böylece her zaman bir döngüde beklemeleri önerilir.

ama daha sonra

Sahte uyandırma olasılığını ortadan kaldırmak için bir uygulama ücretsizdir

ve AbstractQueuedSynchronizer's Conditionarayüzü tam olarak bunu yapar - sahte uyandırma olasılığını ortadan kaldırır .

Bu elbette diğerlerinin ConditionObjectbeklediği yöntemler için de geçerlidir .

Yani, sonuç, geçerli:

her zaman Condition.awaitdöngüyü çağırmalı ve koşulun tutulup tutulmadığını kontrol etmeliyiz , ancak standart, OpenJDK ile Java Sınıf Kütüphanesi asla gerçekleşemez . Yine, çok alışılmadık Java Sınıf Kütüphanesi (bu çok çok sıradışı olmalı, çünkü şu anda neredeyse tükenmiş GNU Sınıfyolu ve Apache Harmony gibi iyi bilinen başka bir OpenJDK Java Sınıf Kütüphanesi, standart Conditionarayüz uygulamasına özdeş görünüyor )

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.