2013-02-27 200 views
5

यह article जावा के "सिंक्रनाइज़" कीवर्ड के बारे में वार्तालाप करता है।जावा थ्रेड "सिंक्रनाइज़"

... 
    private int foo; 
    public synchronized int getFoo() { return foo; } 
    public synchronized void setFoo(int f) { foo = f; } 

एक फोन करने वाले foo संपत्ति बढ़ाने के लिए चाहता है, ऐसा करने के लिए निम्न कोड थ्रेड-सुरक्षित नहीं है:

... 
    setFoo(getFoo() + 1); 

दो धागे एक ही समय में foo बढ़ाने के लिए प्रयास करते हैं, नतीजा यह हो सकता है कि समय के आधार पर foo का मूल्य एक या दो से बढ़ जाता है।

अब, मेरे सवाल:

क्यों setFoo पर "सिंक्रनाइज़" नहीं करता है() के ऊपर बोल्ड लाइन को रोकने?

उत्तर

6

यह चेक-अप-एक्ट रेस हालत का एक उदाहरण है।

एक परिदृश्य निम्नलिखित की तरह हो सकता है:

Thread-1 getFoo() returns 0 
Thread-2 getFoo() returns 0 
Thread-2 setFoo(1) 
Thread-1 setFoo(1) 

इसका मतलब यह होगा कि दो धागे foo बढ़ाने के लिए प्रयास किया है, लेकिन यह केवल एक बार वृद्धि होने का प्रभाव पड़ता है।

अन्य उत्तर की पहचान की है के रूप में, getFoo() और setFoo (के रूप में एक ही वस्तु पर एक तुल्यकालन ब्लॉक लॉकिंग के साथ incrementation सिंक्रनाइज़ किया जा रहा है) इस रेस स्थिति को रोकने क्योंकि धागे से ऊपर की तरह बिछा करने में सक्षम नहीं होगा।

6

क्योंकि आप की गारंटी और कोई नहीं आप के साथ foo हो रही है, और कि कोई और आप के अलावा foo स्थापित कर रही है वापस, लेकिन आप नहीं इसकी गारंटी कोई नहीं अंदर और बाहर (या में सिर्फ) पाने में कामयाब रहे आप कॉल के बीच मिल() और आप तैयार कॉल()

आपको लगता है कि कोड के रूप में पूरी तरह से इस के बराबर के बारे में सोच सकते हैं:

int temp = getFoo(); //safe method 
temp = temp+1; //not protected here - im not holding any locks ... 
setFoo(temp); //safe method 
4

दोनों तरीकों पर synchronized कीवर्ड इसे सुरक्षित थ्रेड नहीं है, क्योंकि एक धागा सकता है getFoo पर कॉल करें, फिर दूसरा धागापर कॉल कर सकता है, और उनमें से प्रत्येक एक ही परिणाम मिलता है। फिर उनमें से प्रत्येक एक को जोड़ता है और setFoo पर कॉल करता है, और अंत परिणाम यह है कि foo दो बार की बजाय केवल एक बार बढ़ता है। जैसा कि आपका आलेख बताता है, यह दौड़ की स्थिति है।

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

public synchronized void addFoo(int addend) 
{ 
    foo += addend; 
} 
+0

'फिर उनमें से प्रत्येक एक को जोड़ता है और सेटफू को कॉल करता है, और अंत परिणाम यह है कि foo केवल दो बार की बजाय बढ़ता है, क्यों? –

+0

क्योंकि प्रत्येक थ्रेड एक ही मान पर 'foo' अपडेट करता है। उदाहरण के लिए, उनमें से प्रत्येक को मान 2 मिल जाता है, उनमें से प्रत्येक 3 प्राप्त करने के लिए 1 जोड़ता है, फिर उनमें से प्रत्येक मान को 3. – rgettman

+0

पर सेट करता है यदि मैं सही समझता हूं, तो आपका कथन नहीं होना चाहिए 'फिर उनमें से प्रत्येक एक को जोड़ना चाहिए और setFoo को कॉल करें, और अंतिम परिणाम यह है कि foo बढ़ी ** ** दो बार **? –

1

आपके कोड में मुख्य जाल यह है कि ऐसा लगता है कि getFoo को "अंदर" setFoo कहा जाएगा।

setFoo(){ 
    //getFoo(); 
    //... 
} 

का प्रकार जो क्योंकि वास्तविकता getFoo में गलत है setFoo कॉल करने से पहले कहा जाता है।यहाँ उदाहरण है कि यह पता चलता है:

public static int foo(int i) { 
    System.out.print("FOO!"); 
    return i; 
} 

public static int bar(int i) { 
    System.out.print("BAR!"); 
    return i; 
} 

public static void main(String[] args) throws Exception { 
    System.out.println(foo(bar(1))); 
} 

आउटपुट:

BAR!FOO!1 

आप देख सकते हैं barfoo से पहले लागू किया गया था। तो आपके मामले में यह संभव है कि दो (या अधिक) थ्रेड getFoo का आह्वान करेंगे जो setFoo पर कॉल करने से पहले वर्तमान मान वापस कर देगा। इस मामले में वे दोनों एक ही मूल्य है होगा, मान लीजिए कि 0 और वे दोनों यह 1.

0

करता है का उपयोग नहीं कर सकते करने के लिए सेट हो जाएगा, जब वे setFoo कॉल करेंगे कोड मदद?

class C { 
    private int foo; 
    public int getFoo() { return foo; } 
    public void setFoo(int f) { foo = f; } 
} 

C myC = new C(); 
synchronized(myC) { 
    int foo = myC.getFoo(); 
    myC.setFoo(foo + 1); 
} 
println(myC.foo); 
+0

तो बिंदु यह है कि ' myC.setFoo (1) '' println (myC.getFoo()) 'से पहले कॉल किया जा सकता है? –

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^