क्या = = थ्रेड सुरक्षित है?


140

मुझे पता है कि यौगिक ऑपरेशन जैसे i++थ्रेड सुरक्षित नहीं हैं क्योंकि वे कई ऑपरेशन शामिल करते हैं

लेकिन अपने आप को एक थ्रेड सुरक्षित संचालन के साथ संदर्भ की जांच कर रहा है?

a != a //is this thread-safe

मैंने इसे प्रोग्राम करने और कई थ्रेड्स का उपयोग करने की कोशिश की लेकिन यह विफल नहीं हुआ। मुझे लगता है कि मैं अपनी मशीन पर दौड़ का अनुकरण नहीं कर सकता।

संपादित करें:

public class TestThreadSafety {
    private Object a = new Object();

    public static void main(String[] args) {

        final TestThreadSafety instance = new TestThreadSafety();

        Thread testingReferenceThread = new Thread(new Runnable() {

            @Override
            public void run() {
                long countOfIterations = 0L;
                while(true){
                    boolean flag = instance.a != instance.a;
                    if(flag)
                        System.out.println(countOfIterations + ":" + flag);

                    countOfIterations++;
                }
            }
        });

        Thread updatingReferenceThread = new Thread(new Runnable() {

            @Override
            public void run() {
                while(true){
                    instance.a = new Object();
                }
            }
        });

        testingReferenceThread.start();
        updatingReferenceThread.start();
    }

}

यह प्रोग्राम है जो मैं थ्रेड-सुरक्षा का परीक्षण करने के लिए उपयोग कर रहा हूं।

अजीब व्यवहार

जैसा कि मेरा कार्यक्रम कुछ पुनरावृत्तियों के बीच शुरू होता है, मुझे आउटपुट फ़्लैग मूल्य मिलता है, जिसका अर्थ है कि संदर्भ !=चेक उसी संदर्भ पर विफल रहता है। लेकिन कुछ पुनरावृत्तियों के बाद आउटपुट निरंतर मूल्य बन जाता है falseऔर फिर लंबे समय तक प्रोग्राम को निष्पादित करने से एक भी trueआउटपुट उत्पन्न नहीं होता है ।

जैसा कि आउटपुट कुछ n (निश्चित नहीं) पुनरावृत्तियों के बाद सुझाव देता है कि आउटपुट निरंतर मूल्य प्रतीत होता है और परिवर्तित नहीं होता है।

आउटपुट:

कुछ पुनरावृत्तियों के लिए:

1494:true
1495:true
1496:true
19970:true
19972:true
19974:true
//after this there is not a single instance when the condition becomes true

2
इस संदर्भ में "थ्रेड-सेफ" से आपका क्या अभिप्राय है? क्या आप पूछ रहे हैं कि क्या यह हमेशा झूठे वापस करने की गारंटी है?
जेबी निज़ेट

@JBNizet हाँ। यही मैं सोच रहा था।
नरेंद्र पथाई

5
यह हमेशा एकल-थ्रेडेड संदर्भ में गलत नहीं होता है। यह एक NaN हो सकता है ..
हेरोल्ड

4
संभावित स्पष्टीकरण: कोड केवल-इन-टाइम संकलित किया गया था और संकलित कोड चर को केवल एक बार लोड करता है। यह अपेक्षित है।
मार्को टोपोलनिक

3
व्यक्तिगत परिणामों को प्रिंट करना दौड़ के लिए परीक्षण करने का एक खराब तरीका है। मुद्रण (दोनों स्वरूपण और परिणाम लिखना) आपके परीक्षण की तुलना में अपेक्षाकृत महंगा है (और कभी-कभी आपका कार्यक्रम लेखन पर अवरुद्ध हो जाएगा जब टर्मिनल या टर्मिनल के कनेक्शन का बैंडविथ धीमा होता है)। इसके अलावा, IO में अक्सर अपने स्वयं के म्यूटेक्स होते हैं जो आपके थ्रेड्स के निष्पादन क्रम को अनुमति देगा (ध्यान दें कि आपकी व्यक्तिगत रेखाएं 1234:trueकभी भी एक-दूसरे को नष्ट नहीं करती हैं )। एक दौड़ परीक्षा में एक सख्त आंतरिक लूप की आवश्यकता होती है। अंत में एक सारांश प्रिंट करें (जैसा कि किसी ने एक इकाई परीक्षण ढांचे के साथ नीचे किया था)।
बेन जैक्सन

जवाबों:


124

सिंक्रनाइज़ेशन की अनुपस्थिति में यह कोड

Object a;

public boolean test() {
    return a != a;
}

उत्पादन कर सकते हैं true। इस के लिए bytecode हैtest()

    ALOAD 0
    GETFIELD test/Test1.a : Ljava/lang/Object;
    ALOAD 0
    GETFIELD test/Test1.a : Ljava/lang/Object;
    IF_ACMPEQ L1
...

जैसा कि हम देख सकते हैं कि यह क्षेत्र aको दो बार स्थानीय संस्करण में लोड करता है , यह एक गैर-परमाणु संचालन है, अगर aकिसी अन्य थ्रेड तुलना के बीच में परिवर्तन किया गया हो सकता है false

इसके अलावा, मेमोरी दृश्यता समस्या यहां प्रासंगिक है, इस बात की कोई गारंटी नहीं है कि aकिसी अन्य थ्रेड द्वारा किए गए परिवर्तन वर्तमान थ्रेड को दिखाई देंगे।


22
हालांकि मजबूत सबूत, बायटेकोड वास्तव में एक सबूत नहीं है। यह जेएलएस में भी कहीं न कहीं होना चाहिए ...
मार्को टोपोलनिक

10
@ मैर्को मैं आपकी सोच से सहमत हूं, लेकिन जरूरी नहीं कि आपका निष्कर्ष। मेरे लिए उपर्युक्त बाईटेकोड लागू करने का स्पष्ट / विहित तरीका है !=, जिसमें एलएचएस और आरएचएस को अलग-अलग लोड करना शामिल है। और इसलिए यदि LLS और RHS क्रमिक रूप से समान हैं, तो JLS को ऑप्टिमाइज़ेशन के बारे में कुछ विशेष का उल्लेख नहीं है , तो सामान्य नियम लागू होगा, जिसका अर्थ है aदो बार लोड करना ।
आंद्रेज डोयले

20
दरअसल, यह मानते हुए कि उत्पन्न बायोटेक जेएलएस के अनुरूप है, यह एक प्रमाण है!
प्रस्कर

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

2
@AlexanderTorstling - "मुझे यकीन नहीं है कि अगर एक एकल-पठन अनुकूलन को शामिल करने के लिए पर्याप्त है।" यह पर्याप्त नहीं है। वास्तव में, सिंक्रनाइज़ेशन की अनुपस्थिति में (और अतिरिक्त "" रिश्तों को लागू करने से पहले होता है), अनुकूलन मान्य है,
स्टीफन सी

47

क्या चेक a != aथ्रेड सुरक्षित है?

यदि aसंभावित रूप से किसी अन्य थ्रेड द्वारा (उचित सिंक्रनाइज़ेशन के बिना!) अपडेट किया जा सकता है, तो नहीं।

मैंने इसे प्रोग्राम करने और कई थ्रेड्स का उपयोग करने की कोशिश की, लेकिन असफल नहीं हुआ। मुझे लगता है कि मेरी मशीन पर दौड़ का अनुकरण नहीं किया जा सकता है।

इसका कोई मतलब नहीं है! मुद्दा यह है कि यदि एक निष्पादन जिसमें aकिसी अन्य थ्रेड द्वारा अद्यतन किया जाता है, JLS द्वारा अनुमति दी जाती है, तो कोड थ्रेड-सुरक्षित नहीं है। तथ्य यह है कि आप दौड़ की स्थिति को किसी विशेष मशीन और एक विशेष जावा कार्यान्वयन पर एक विशेष परीक्षण-केस के साथ होने का कारण नहीं बन सकते हैं, यह अन्य परिस्थितियों में होने से रोकता नहीं है।

क्या इसका मतलब यह है कि एक =! लौट सकता है true

हाँ, सिद्धांत में, कुछ परिस्थितियों में।

वैकल्पिक रूप से, एक साथ बदल रहा था , भले ही a != aवापस आ सकता है ।falsea


"अजीब व्यवहार" के बारे में:

जैसा कि मेरा कार्यक्रम कुछ पुनरावृत्तियों के बीच शुरू होता है, मुझे आउटपुट फ़्लैग मान मिलता है, जिसका अर्थ है कि संदर्भ! = चेक उसी संदर्भ में विफल हो जाता है। लेकिन कुछ पुनरावृत्तियों के बाद आउटपुट निरंतर मूल्य गलत हो जाता है और फिर लंबे समय तक प्रोग्राम को निष्पादित करने से एक भी वास्तविक आउटपुट उत्पन्न नहीं होता है।

यह "अजीब" व्यवहार निम्नलिखित निष्पादन परिदृश्य के अनुरूप है:

  1. कार्यक्रम लोड हो गया है और जेवीएम ने बायोटेक की व्याख्या करना शुरू कर दिया है। चूंकि (जैसा कि हमने javap आउटपुट से देखा है) bytecode दो भार करता है, आप (जाहिरा तौर पर) दौड़ की स्थिति के परिणाम कभी-कभी देखते हैं।

  2. एक समय के बाद, कोड को JIT कंपाइलर द्वारा संकलित किया जाता है। जेआईटी ऑप्टिमाइज़र ने नोटिस किया कि एक ही मेमोरी स्लॉट के दो लोड ( a) एक साथ करीब हैं, और दूसरा एक दूर का अनुकूलन करता है। (वास्तव में, एक मौका है कि यह पूरी तरह से परीक्षण का अनुकूलन करता है ...)

  3. अब दौड़ की स्थिति अब प्रकट नहीं होती है, क्योंकि अब दो भार नहीं हैं।

ध्यान दें कि यह सब जेएलएस जावा को लागू करने की अनुमति देता है के अनुरूप है।


इस प्रकार @kriss ने टिप्पणी की:

यह इस तरह दिखता है कि सी या सी ++ प्रोग्रामर "अनडिफ़ाइंड बिहेवियर" (कार्यान्वयन निर्भर) कह सकते हैं। लगता है कि इस तरह से कोने के मामलों में जावा में कुछ यूबी हो सकता है।

जावा मेमोरी मॉडल ( जेएलएस 17.4 में निर्दिष्ट) पूर्व शर्त का एक सेट निर्दिष्ट करता है जिसके तहत एक धागा को दूसरे धागे द्वारा लिखे गए स्मृति मूल्यों को देखने की गारंटी है। यदि एक धागा दूसरे द्वारा लिखे गए चर को पढ़ने का प्रयास करता है, और उन पूर्व शर्त से संतुष्ट नहीं हैं, तो कई संभावित निष्पादन हो सकते हैं ... जिनमें से कुछ गलत होने की संभावना है (आवेदन की आवश्यकताओं के दृष्टिकोण से)। दूसरे शब्दों में, सेट संभव व्यवहार के परिभाषित किया गया है ( "अच्छी तरह से गठित सज़ाएँ" के सेट यानी), लेकिन हम यह नहीं कह सकते जो उन व्यवहार की हो जाएगा।

संकलक को लोड को जोड़ने और पुनः व्यवस्थित करने और (और अन्य कार्य करने) की अनुमति है बशर्ते कोड का अंतिम प्रभाव समान हो:

  • जब एक ही धागे द्वारा निष्पादित किया जाता है, और
  • जब अलग-अलग थ्रेड द्वारा निष्पादित किया जाता है जो सही ढंग से सिंक्रनाइज़ होता है (मेमोरी मॉडल के अनुसार)।

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


क्या इसका मतलब यह है कि a != aयह सच हो सकता है?
प्रस्कर

मेरा मतलब था कि शायद मेरी मशीन पर मैं यह अनुकरण नहीं कर सकता था कि उपरोक्त कोड गैर-थ्रेड सुरक्षित है। तो शायद इसके पीछे कोई सैद्धांतिक तर्क हो।
नरेन्द्र पथाई

@ नरेंद्रपति - कोई सैद्धांतिक कारण नहीं है कि आप इसे प्रदर्शित क्यों नहीं कर सकते। संभवतः एक व्यावहारिक कारण है ... या शायद आप भाग्यशाली नहीं थे।
स्टीफन सी

कृपया मेरे अपडेट किए गए उत्तर को उस प्रोग्राम के साथ जांचें जो मैं उपयोग कर रहा हूं। चेक कभी-कभी सच हो जाता है लेकिन आउटपुट में अजीब व्यवहार होने लगता है।
नरेंद्र पथाई

1
@ नरेंद्रपति - मेरी व्याख्या देखें।
स्टीफन सी

27

परीक्षण-एनजी के साथ साबित:

public class MyTest {

  private static Integer count=1;

  @Test(threadPoolSize = 1000, invocationCount=10000)
  public void test(){
    count = new Integer(new Random().nextInt());
    Assert.assertFalse(count != count);
  }

}

मेरे 10 000 चालान पर 2 विफल हैं। तो नहीं , यह धागा सुरक्षित नहीं है


6
आप समानता के लिए भी जाँच नहीं कर रहे हैं ... Random.nextInt()हिस्सा बहुत अधिक है। आप new Object()बस के साथ भी परीक्षण कर सकते थे ।
मार्को टोपोलनिक

@MarkoTopolnik कृपया उस प्रोग्राम के साथ मेरे अपडेट किए गए उत्तर की जांच करें जिसका मैं उपयोग कर रहा हूं। चेक कभी-कभी सच हो जाता है लेकिन आउटपुट में अजीब व्यवहार होने लगता है।
नरेंद्र पथाई

1
एक साइड नोट, रैंडम-ऑब्जेक्ट आमतौर पर पुन: उपयोग किए जाने के लिए होते हैं, हर बार जब आपको नए इंट की आवश्यकता नहीं होती है।
साइमन फोर्सबर्ग

15

नहीं ऐसा नहीं है। तुलना के लिए जावा वीएम को स्टैक पर तुलना करने के लिए दो मूल्यों को रखना चाहिए और तुलना निर्देश को चलाना चाहिए (जो कि "" ए "के प्रकार पर निर्भर करता है)।

जावा वीएम हो सकता है:

  1. "ए" दो बार पढ़ें, प्रत्येक को स्टैक पर रखें और फिर परिणामों की तुलना करें
  2. "केवल" एक बार पढ़ें, इसे स्टैक पर रखें, इसे डुप्लिकेट करें ("डुप" निर्देश) और तुलना चलाएं
  3. अभिव्यक्ति को पूरी तरह से हटा दें और इसे बदलें false

1 मामले में, एक और धागा दो रीड्स के बीच "ए" के लिए मूल्य को संशोधित कर सकता है।

कौन सी रणनीति चुनी जाती है यह जावा कंपाइलर और जावा रनटाइम (विशेषकर जेआईटी कंपाइलर) पर निर्भर करता है। यह आपके प्रोग्राम के रनटाइम के दौरान भी बदल सकता है।

यदि आप यह सुनिश्चित करना चाहते हैं कि चर कैसे एक्सेस किया जाता है, तो आपको इसे volatile(तथाकथित "आधा मेमोरी बैरियर") बनाना होगा या पूर्ण मेमोरी बैरियर ( synchronized) जोड़ना होगा । तुम भी कुछ hgiher स्तर एपीआई (जैसे AtomicIntegerकि जुनैद अहसन द्वारा उल्लिखित) का उपयोग कर सकते हैं ।

थ्रेड सुरक्षा के बारे में जानकारी के लिए, JSR 133 ( जावा मेमोरी मॉडल ) पढ़ें ।


aजैसा volatileकि बीच में बदलाव की संभावना के साथ घोषणा करना अभी भी दो अलग-अलग अर्थों को समझाएगा ।
होल्गर

6

यह सब स्टीफन सी द्वारा अच्छी तरह से समझाया गया है। मज़े के लिए, आप निम्नलिखित जेवीएम मापदंडों के साथ एक ही कोड चलाने की कोशिश कर सकते हैं:

-XX:InlineSmallCode=0

यह जेआईटी द्वारा किए गए अनुकूलन को रोकता है (यह हॉटस्पॉट 7 सर्वर पर करता है) और आप trueहमेशा के लिए देखेंगे (मुझे 2,000,000 पर रोक दिया गया था लेकिन मुझे लगता है कि यह उसके बाद भी जारी है)।

जानकारी के लिए, नीचे JIT'ed कोड है। सच कहूं तो, मैं यह जानने के लिए पर्याप्त रूप से विधानसभा नहीं पढ़ता हूं कि क्या परीक्षण वास्तव में किया गया है या दो भार कहां से आए हैं। (लाइन 26 का परीक्षण है flag = a != aऔर लाइन 31 का समापन ब्रेस है while(true))।

  # {method} 'run' '()V' in 'javaapplication27/TestThreadSafety$1'
  0x00000000027dcc80: int3   
  0x00000000027dcc81: data32 data32 nop WORD PTR [rax+rax*1+0x0]
  0x00000000027dcc8c: data32 data32 xchg ax,ax
  0x00000000027dcc90: mov    DWORD PTR [rsp-0x6000],eax
  0x00000000027dcc97: push   rbp
  0x00000000027dcc98: sub    rsp,0x40
  0x00000000027dcc9c: mov    rbx,QWORD PTR [rdx+0x8]
  0x00000000027dcca0: mov    rbp,QWORD PTR [rdx+0x18]
  0x00000000027dcca4: mov    rcx,rdx
  0x00000000027dcca7: movabs r10,0x6e1a7680
  0x00000000027dccb1: call   r10
  0x00000000027dccb4: test   rbp,rbp
  0x00000000027dccb7: je     0x00000000027dccdd
  0x00000000027dccb9: mov    r10d,DWORD PTR [rbp+0x8]
  0x00000000027dccbd: cmp    r10d,0xefc158f4    ;   {oop('javaapplication27/TestThreadSafety$1')}
  0x00000000027dccc4: jne    0x00000000027dccf1
  0x00000000027dccc6: test   rbp,rbp
  0x00000000027dccc9: je     0x00000000027dcce1
  0x00000000027dcccb: cmp    r12d,DWORD PTR [rbp+0xc]
  0x00000000027dcccf: je     0x00000000027dcce1  ;*goto
                                                ; - javaapplication27.TestThreadSafety$1::run@62 (line 31)
  0x00000000027dccd1: add    rbx,0x1            ; OopMap{rbp=Oop off=85}
                                                ;*goto
                                                ; - javaapplication27.TestThreadSafety$1::run@62 (line 31)
  0x00000000027dccd5: test   DWORD PTR [rip+0xfffffffffdb53325],eax        # 0x0000000000330000
                                                ;*goto
                                                ; - javaapplication27.TestThreadSafety$1::run@62 (line 31)
                                                ;   {poll}
  0x00000000027dccdb: jmp    0x00000000027dccd1
  0x00000000027dccdd: xor    ebp,ebp
  0x00000000027dccdf: jmp    0x00000000027dccc6
  0x00000000027dcce1: mov    edx,0xffffff86
  0x00000000027dcce6: mov    QWORD PTR [rsp+0x20],rbx
  0x00000000027dcceb: call   0x00000000027a90a0  ; OopMap{rbp=Oop off=112}
                                                ;*aload_0
                                                ; - javaapplication27.TestThreadSafety$1::run@2 (line 26)
                                                ;   {runtime_call}
  0x00000000027dccf0: int3   
  0x00000000027dccf1: mov    edx,0xffffffad
  0x00000000027dccf6: mov    QWORD PTR [rsp+0x20],rbx
  0x00000000027dccfb: call   0x00000000027a90a0  ; OopMap{rbp=Oop off=128}
                                                ;*aload_0
                                                ; - javaapplication27.TestThreadSafety$1::run@2 (line 26)
                                                ;   {runtime_call}
  0x00000000027dcd00: int3                      ;*aload_0
                                                ; - javaapplication27.TestThreadSafety$1::run@2 (line 26)
  0x00000000027dcd01: int3   

1
यह उस तरह के कोड का एक अच्छा उदाहरण है जो वास्तव में JVM का उत्पादन करेगा जब आपके पास एक अनंत लूप होगा और सब कुछ कम या ज्यादा फहराया जा सकता है। वास्तविक "लूप" यहां से तीन निर्देश दिए गए हैं 0x27dccd1करने के लिए 0x27dccdfjmpपाश में बिना शर्त है (के बाद से पाश अनंत है)। लूप में केवल दो अन्य निर्देश हैं add rbc, 0x1- जो वेतन वृद्धि है countOfIterations(इस तथ्य के बावजूद कि लूप कभी भी बाहर नहीं निकलेगा, इसलिए यह मान नहीं पढ़ा जाएगा: शायद यह डीबगर में इसे तोड़ने की स्थिति में आवश्यक है), ।। ।
बीऑनरोप

... और अजीब दिखने वाला testनिर्देश, जो वास्तव में केवल मेमोरी एक्सेस के लिए है (ध्यान दें कि eaxयह विधि में कभी भी सेट नहीं है!): यह एक विशेष पृष्ठ है जो जेवीएम के सभी थ्रेड को ट्रिगर करने के लिए नहीं पठनीय है। एक सुरक्षित स्थान पर पहुंचने के लिए, इसलिए यह gc या कुछ अन्य ऑपरेशन कर सकता है जिसके लिए सभी थ्रेड्स को ज्ञात अवस्था में होना आवश्यक है।
बीयॉनरोप

इस बिंदु पर अधिक, JVM ने instance. a != instance.aलूप से तुलना को पूरी तरह से फहराया , और लूप में प्रवेश करने से पहले केवल एक बार इसे करता है! यह जानता है कि इसे फिर से लोड करने की आवश्यकता नहीं है instanceया aजैसा कि उन्हें अस्थिर घोषित नहीं किया गया है और कोई अन्य कोड नहीं है जो उन्हें एक ही धागे पर बदल सकता है, इसलिए यह मान लेता है कि वे पूरे लूप के दौरान समान हैं, जो मेमोरी द्वारा अनुमत है नमूना।
बीऑनरोप

5

नहीं, a != aधागा सुरक्षित नहीं है। इस अभिव्यक्ति में तीन भाग होते हैं: लोड करना a, aफिर से लोड करना और प्रदर्शन करना !=। एक और सूत्र के लिए यह संभव है कि वह अपने aमाता-पिता के आंतरिक aभार को पाले और 2 लोड ऑपरेशनों के बीच के मूल्य को बदले ।

एक अन्य कारक हालांकि aस्थानीय है। यदि aस्थानीय है तो किसी अन्य धागे की पहुंच नहीं होनी चाहिए और इसलिए धागा सुरक्षित होना चाहिए।

void method () {
    int a = 0;
    System.out.println(a != a);
}

हमेशा प्रिंट करना चाहिए false

aजैसा volatileकि aहै staticया उदाहरण के लिए घोषित करने से समस्या का समाधान नहीं होगा । समस्या यह नहीं है कि थ्रेड्स के अलग-अलग मूल्य हैं a, लेकिन यह कि एक थ्रेड aविभिन्न मूल्यों के साथ दो बार लोड होता है । यह वास्तव में केस को थ्रेड-सेफ बना सकता है .. यदि तब कैश aनहीं किया जा सकता है और दूसरे थ्रेड में परिवर्तन कैश्ड मान को प्रभावित नहीं करेगा।volatilea


इसके साथ आपका उदाहरण synchronizedगलत है: उस कोड को प्रिंट करने की गारंटी के लिए false, सेट करने वाले सभी तरीकों aको synchronizedभी करना होगा।
बरबाद

ऐसा क्यों? यदि विधि को सिंक्रनाइज़ किया जाता है, तो कोई अन्य थ्रेड आंतरिक aअभिभावक को कैसे प्राप्त होगा, जबकि विधि निष्पादित हो रही है, मूल्य निर्धारित करने के लिए आवश्यक है a
DoubleMx2

1
आपका परिसर गलत है। आप इसके आंतरिक लॉक को प्राप्त किए बिना किसी ऑब्जेक्ट के क्षेत्र को सेट कर सकते हैं। जावा को अपने खेतों को सेट करने से पहले किसी ऑब्जेक्ट के आंतरिक लॉक का अधिग्रहण करने के लिए एक धागे की आवश्यकता नहीं होती है।
बरबाद करें

3

अजीब व्यवहार के बारे में:

चूंकि चर aके रूप में चिह्नित नहीं किया गया है volatile, कुछ बिंदु पर यह aथ्रेड द्वारा कैश किया जा सकता है। दोनों aएस a != aकैश्ड वर्जन हैं और इस तरह हमेशा एक ही हैं (मतलब flagअब हमेशा होता है false)।


0

साधारण पढ़ा हुआ भी परमाणु नहीं है। तो aहै longऔर के रूप में चिह्नित नहीं volatile32-बिट JVMs पर तो long b = aथ्रेड-सुरक्षित नहीं है।


अस्थिरता और परमाणुवाद असंबंधित हैं। यहां तक ​​कि अगर मैं एक अस्थिर को चिह्नित करता हूं, तो यह गैर-परमाणु होगा
नरेंद्र पथाई

एक अस्थिर लंबे क्षेत्र का काम हमेशा परमाणु होता है। ++ जैसे अन्य ऑपरेशन नहीं हैं।
ZhekaKozlov
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.