क्या वास्तव में जावा में अजीब वेकअप होता है?


208

विभिन्न लॉकिंग से संबंधित प्रश्न और (लगभग) हमेशा 'स्पूर्ड वेकअप्स ’के कारण लूप को ढूंढना’ 1 I आश्चर्य है, क्या किसी ने इस तरह के वेकअप का अनुभव किया है (उदाहरण के लिए एक सभ्य हार्डवेयर / सॉफ्टवेयर वातावरण मानकर)?

मुझे पता है कि 'स्प्रिचुअल' शब्द का कोई स्पष्ट कारण नहीं है लेकिन इस तरह के आयोजन के क्या कारण हो सकते हैं?

( 1 नोट: मैं लूपिंग अभ्यास पर सवाल नहीं उठा रहा हूं।)

संपादित करें: एक सहायक प्रश्न (कोड नमूने पसंद करने वालों के लिए):

यदि मेरे पास निम्न कार्यक्रम है, और मैं इसे चलाता हूं:

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

awaitएक यादृच्छिक घटना के लिए हमेशा इंतजार किए बिना मैं इसे जगाने के लिए क्या कर सकता हूं ?


1
JVM के लिए जो POSIX सिस्टम पर चलते हैं pthread_cond_wait()और असली सवाल का उपयोग करते हैं, "pthread_cond_wait में क्यों अजीब जगहें हैं?"
प्रवाह

जवाबों:


204

विचित्र लेखों पर विकिपीडिया लेख में यह tidbit है:

pthread_cond_wait()लिनक्स में समारोह का उपयोग कर कार्यान्वित किया जाता है futexसिस्टम कॉल। EINTRजब सिस्टम एक सिग्नल प्राप्त करता है तो लिनक्स पर प्रत्येक ब्लॉकिंग सिस्टम कॉल अचानक से लौटता है । ... pthread_cond_wait()प्रतीक्षा को पुनः आरंभ नहीं किया जा सकता क्योंकि यह futexसिस्टम कॉल के बाहर होने के समय में एक वास्तविक वेकअप को याद कर सकता है । इस दौड़ की स्थिति को केवल एक अपरिवर्तनीयता के लिए कॉलर की जाँच से बचा जा सकता है। इसलिए POSIX सिग्नल एक स्पुरियस वेकअप पैदा करेगा।

सारांश : यदि एक लिनक्स प्रक्रिया का संकेत दिया जाता है तो इसके वेटिंग थ्रेड्स का आनंद प्रत्येक को एक अच्छा, हॉट स्पुरियस वेकअप मिलेगा ।

समझ गया। अक्सर दी जाने वाली "प्रदर्शन के लिए यह अस्पष्ट" की तुलना में निगलने के लिए एक आसान गोली है जो अक्सर दी जाती है।


13
यहाँ बेहतर व्याख्या: stackoverflow.com/questions/1461913/…
गिल्ली

3
यह EINTR अनब्लॉक यूनिक्स व्युत्पन्न सिस्टम में सभी ब्लॉकिंग सिस्टम कॉल के लिए सही है। इसने कर्नेल को बहुत सरल बना दिया, लेकिन अनुप्रयोग प्रोग्रामर ने बोझ खरीद लिया।
टिम विस्क्रॉफ्ट

2
मैंने सोचा कि pthread_cond_wait () और दोस्त EINTR नहीं लौटा सकते, लेकिन अगर जीरो हो जाए तो जीरो वापस कर सकते हैं? प्रेषक: pubs.opengroup.org/onlinepubs/7908799/xsh/… "ये फ़ंक्शन [EINTR] का एक त्रुटि कोड नहीं लौटाएंगे।"
गब्बी

2
@jgubby यह सही है। अंतर्निहित futex()कॉल रिटर्न EINTR, लेकिन वह रिटर्न वैल्यू अगले स्तर तक बुदबुदाती नहीं है। इसलिए प्लीथ कॉल करने वाले को एक अयोग्य के लिए जाँच करनी चाहिए। वे जो कह रहे हैं, वह यह है कि जब pthread_cond_wait()आप अपनी लूप कंडीशन (अनियंत्रित) को फिर से जांचते हैं, क्योंकि इंतजार शायद ही जागेगा। सिस्टम कॉल के दौरान सिग्नल प्राप्त करना एक संभावित कारण है, लेकिन यह केवल एक ही नहीं है।
जॉन कुगेलमैन

1
संभवतः, pthreadलाइब्रेरी अपने स्वयं के अपरिवर्तनीय और अपने स्वयं के जाँच तर्क की आपूर्ति कर सकती है ताकि उपयोगकर्ता पर उस ज़िम्मेदारी को पारित करने के बजाए नकली जागनों को समाप्त किया जा सके। (संभवतः) का दावा किया गया प्रदर्शन प्रभाव होगा।

22

मेरे पास एक उत्पादन प्रणाली है जो इस व्यवहार को प्रदर्शित करती है। एक थ्रेड सिग्नल पर प्रतीक्षा करता है कि कतार में एक संदेश है। व्यस्त अवधि में, 20% तक वेकप्स स्पुरियस होते हैं (यानी जब वह उठता है तो कतार में कुछ भी नहीं होता है)। यह थ्रेड संदेशों का एकमात्र उपभोक्ता है। यह लिनक्स SLES-10 8-प्रोसेसर बॉक्स पर चलता है और इसे GCC 4.1.2 के साथ बनाया गया है। संदेश एक बाहरी स्रोत से आते हैं और अतुल्यकालिक रूप से संसाधित होते हैं क्योंकि समस्याएं हैं यदि मेरा सिस्टम उन्हें पर्याप्त तेजी से नहीं पढ़ता है।


15

शीर्षक में प्रश्न का उत्तर देने के लिए - हाँ! ऐसा होता है। हालांकि, विकी लेख में इस बात का जिक्र किया गया है कि बहुत ही अजीबोगरीब वाक्यों के बारे में एक अच्छी व्याख्या है, जो मेरे सामने आई।

जरा सोचिए ... किसी भी कोड की तरह, थ्रेड शेड्यूलर अंतर्निहित हार्डवेयर / सॉफ़्टवेयर में कुछ असामान्य होने के कारण अस्थायी ब्लैकआउट का अनुभव कर सकता है। बेशक, इस बात का ध्यान रखा जाना चाहिए कि ऐसा कम से कम संभव हो, लेकिन चूंकि 100% मजबूत सॉफ़्टवेयर जैसी कोई चीज़ नहीं है, इसलिए यह मान लेना उचित होगा कि अगर ऐसा हो सकता है तो शेड्यूलर का पता लगाने के मामले में अनुग्रहकारी वसूली पर ध्यान दिया जाए (जैसे गायब दिल की धड़कन को देखकर)।

अब, शेड्यूलर कैसे ठीक हो सकता है, इस बात को ध्यान में रखते हुए कि ब्लैकआउट के दौरान वेटिंग थ्रेड्स को सूचित करने के उद्देश्य से कुछ संकेतों को याद किया जा सकता है? यदि शेड्यूलर कुछ भी नहीं करता है, तो "अनलकी" धागे केवल लटकाए जाएंगे, हमेशा के लिए इंतजार करेंगे - इससे बचने के लिए, शेड्यूलर बस सभी वेटिंग थ्रेड्स को एक संकेत भेजेगा।

यह एक "अनुबंध" स्थापित करने के लिए आवश्यक बनाता है कि प्रतीक्षा थ्रेड को बिना किसी कारण के अधिसूचित किया जा सकता है। सटीक होने के लिए, एक कारण होगा - शेड्यूलर ब्लैकआउट - लेकिन चूंकि थ्रेड डिज़ाइन किया गया है (एक अच्छे कारण के लिए) शेड्यूलर आंतरिक कार्यान्वयन विवरण से अनजान होने के कारण, इस कारण को "सहज" के रूप में पेश करने के लिए बेहतर है।

मैं स्रोत से इस उत्तर को पढ़ रहा था और इसे उचित पाया। यह भी पढ़ें

जावा में अजीब वेकअप और उनसे कैसे बचें

पुनश्च: उपरोक्त लिंक मेरे व्यक्तिगत ब्लॉग पर है, जिसमें स्पिरिअस वेकअप के बारे में अतिरिक्त विवरण है।


9

कैमरन प्यूरी ने कुछ समय पहले एक ब्लॉग पोस्ट लिखी थी, जिसमें वह अजीबोगरीब वेकेशन की समस्या से जूझ रहा था। तो हाँ, ऐसा होता है

मैं अनुमान लगा रहा हूं कि जावा में तैनात किए जाने वाले कुछ प्लेटफार्मों की सीमाओं के कारण यह कल्पना (संभावना के रूप में) है? हालाँकि मैं गलत हो सकता हूँ!


मैंने पोस्ट पढ़ी और मुझे एक अनुप्रयोग के परीक्षण के लिए एक परीक्षण के बारे में एक विचार दिया, जिसमें लूपिंग-वेट प्रतिमान के परीक्षण को अनियमित रूप से / नियतांक रूप से जागृत करने के लिए किया गया था। या यह पहले से ही कहीं उपलब्ध है?
एकरनोकड

यह SO पर एक और सवाल है: "क्या एक सख्त वीएम है जिसे परीक्षण के लिए इस्तेमाल किया जा सकता है?"। मैं किसी को थ्रेड-स्थानीय मेमोरी के साथ देखना पसंद करूंगा - मुझे नहीं लगता कि वे अभी तक मौजूद हैं
ox__akes

8

बस इसे जोड़ना है। हां ऐसा होता है और मैंने 24 कोर मशीन (JDK 6) पर बहु-सूत्रण समस्या के कारण की खोज में तीन दिन बिताए। 10 में से 4 ने बिना किसी पैटर्न के अनुभव किया। यह 2 कोर या 8 कोर पर कभी नहीं हुआ।

कुछ ऑनलाइन सामग्री का अध्ययन किया और यह एक जावा समस्या नहीं है, लेकिन एक सामान्य दुर्लभ लेकिन अपेक्षित व्यवहार है।


हेलो रेन्स, क्या आप वहां चल रहे ऐप को विकसित कर रहे हैं? क्या (किया) यह प्रतीक्षा () मेथड कॉलिंग है जबकि लूप बाहरी स्थिति की जाँच कर रहा है क्योंकि यह जावा डॉक्स डॉक्स .oracle.com/javase/6/docs/api/ java/ lang/… में सुझाया गया है ?
गमकिंस

मैंने इसके बारे में लिखा और हाँ समाधान एक स्थिति की जाँच के साथ एक लूप है। मेरी गलती लापता लूप थी ... लेकिन इसलिए मैंने इन वेकअप के बारे में सीखा ... कभी भी दो कोर पर नहीं, अक्सर 24cores blog.xceptance.com/2011/05/06/spurious-wakeup-the-rare-event
ReneS पर

जब मैंने 40+ कोर यूनिक्स सर्वर पर एप्लिकेशन चलाया तो मुझे भी ऐसे ही अनुभव हुए। इसमें अत्यधिक जाग्रत मात्रा थी। - तो, ​​ऐसा लगता है कि स्प्यूरियस वेकअप की मात्रा सिस्टम के प्रोसेसर कोर की मात्रा के सीधे आनुपातिक है।
bvdb

0

https://stackoverflow.com/a/1461956/14731 में इस बात का एक उत्कृष्ट विवरण है कि क्यों आपको स्पॉर्टी वेकअप से बचाव करने की आवश्यकता है भले ही अंतर्निहित ऑपरेटिंग सिस्टम उन्हें ट्रिगर न करे। यह ध्यान रखना दिलचस्प है कि यह स्पष्टीकरण जावा सहित कई प्रोग्रामिंग भाषाओं में लागू होता है।


0

ओपी के सवाल का जवाब दे रहे हैं

एक यादृच्छिक घटना के लिए हमेशा इंतजार किए बिना इस प्रतीक्षा को जगाने के लिए मैं क्या कर सकता हूं?

, कोई भी स्पिरिट वेकअप इस प्रतीक्षित धागे को नहीं जगा सका!

भले ही ओपी के स्निपेट के मामले में किसी विशेष प्लेटफॉर्म पर स्प्यूरियस वेकअप हो या न हो, यह सकारात्मक रूप से असंभव हैCondition.await() वापसी के लिए और " " को देखने के ! आउटपुट स्ट्रीम में।

जब तक आप बहुत ही विदेशी जावा क्लास लाइब्रेरी का उपयोग नहीं कर रहे हैं

इसका कारण यह है मानक, है OpenJDK के ReentrantLockकी विधि newCondition()रिटर्न AbstractQueuedSynchronizer'की रों कार्यान्वयन Conditionइंटरफेस, नेस्ट ConditionObject(वैसे, इसके बारे में केवल कार्यान्वयन है Conditionइस वर्ग के पुस्तकालय में इंटरफ़ेस), और ConditionObjectकी विधि await()ही जाँच करता है कि हालत नहीं है धारण और कोई भी स्पिरिट वेकप इस पद्धति को गलती से वापस करने के लिए मजबूर नहीं कर सकता है।

वैसे, आप इसे स्वयं जांच सकते हैं क्योंकि -बेड AbstractQueuedSynchronizerकार्यान्वयन में शामिल होने के बाद, यह बहुत ही आसान है कि यह बहुत ही अजीब है। AbstractQueuedSynchronizerनिम्न-स्तर LockSupportके तरीकों parkऔर unparkविधियों का उपयोग करता है , और यदि आप LockSupport.unparkप्रतीक्षा कर रहे एक थ्रेड पर आह्वान करते हैं Condition, तो इस क्रिया को एक सहज जागरण से अलग नहीं किया जा सकता है।

ओपी के स्निपेट को थोड़ा सा दर्शाते हुए,

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

, और कोई फर्क नहीं पड़ता कि कितना मुश्किल unparking (मुख्य) धागा प्रतीक्षा करने वाले धागे को जगाने की कोशिश करेगा, Condition.await()इस मामले में विधि कभी वापस नहीं आएगी।

इंटरफ़ेस के जावदॉक में शानदार वेकअप के Conditionतरीकों पर चर्चा की गई Conditionहै । यद्यपि यह कहता है कि,

जब किसी शर्त पर प्रतीक्षा की जाती है, तो एक स्पुरियस वेकअप होने की अनुमति होती है

और वह

यह अनुशंसा की जाती है कि एप्लिकेशन प्रोग्रामर हमेशा यह मान लें कि वे हो सकते हैं और इसलिए हमेशा एक लूप में प्रतीक्षा करें।

लेकिन यह बाद में जोड़ता है

स्पुरियस वेकअप की संभावना को दूर करने के लिए एक कार्यान्वयन मुफ्त है

और इंटरफ़ेस का AbstractQueuedSynchronizerक्रियान्वयन Conditionठीक वही करता है - जो कि उठने की किसी भी संभावना को दूर करता है ।

यह निश्चित रूप से अन्य ConditionObjectप्रतीक्षित विधियों के लिए सही है ।

तो, निष्कर्ष है:

हमें हमेशा Condition.awaitलूप में कॉल करना चाहिए और जांच करनी चाहिए कि क्या स्थिति नहीं है, लेकिन मानक के साथ, OpenJDK, जावा क्लास लाइब्रेरी कभी नहीं हो सकता है । जब तक, फिर से, आप बहुत ही असामान्य जावा क्लास लाइब्रेरी का उपयोग करते हैं (जो बहुत ही असामान्य होना चाहिए, क्योंकि एक अन्य प्रसिद्ध गैर-ओपनजेडके जावा क्लास लाइब्रेरी, वर्तमान में लगभग विलुप्त हो चुकी जीएनयू क्लासपाथ और अपाचे हार्मनी , Conditionइंटरफ़ेस के मानक कार्यान्वयन के समान है )

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.