2013-05-03 8 views
8

मैं एक साधारण थ्रेड-सुरक्षित कक्षा लिखना चाहता हूं जिसे एक पूर्णांक मूल्य निर्धारित करने या प्राप्त करने के लिए उपयोग किया जा सकता है।एक अस्थिर चर का उपयोग कर एक साधारण धागा-सुरक्षित वर्ग कैसे लिखें?

सबसे आसान तरीका है सिंक्रनाइज़ कीवर्ड का उपयोग करने के लिए है:

public class MyIntegerHolder { 

    private Integer value; 

    synchronized public Integer getValue() { 
     return value; 
    } 

    synchronized public void setValue(Integer value) { 
     this.value = value; 
    } 

} 

मैं भी अस्थिर का उपयोग कर की कोशिश कर सकते:

public class MyIntegerHolder { 

    private volatile Integer value; 

    public Integer getValue() { 
     return value; 
    } 

    public void setValue(Integer value) { 
     this.value = value; 
    } 

} 

अस्थिर कीवर्ड धागे से वर्ग है -साफ?

पर विचार करें घटनाओं के निम्न क्रम:

  1. थ्रेड एक 5 पर
  2. थ्रेड बी मूल्य सेट 7.
  3. थ्रेड सेल्सियस के लिए मूल्य सेट मूल्य पढ़ता है।

यह जावा भाषा विशिष्टता से इस प्रकार है कि

  • "1" होता है-पहले "3"
  • "2" होता है-पहले "3"

लेकिन मुझे नहीं लगता कि यह विनिर्देश से कैसे पालन कर सकता है कि "1" होता है- "2" से पहले मुझे संदेह है कि "1" "2" से पहले होता है।

मुझे लगता है धागा सी पढ़ सकते हैं 7 या 5. मुझे लगता है कि अस्थिर कीवर्ड के साथ वर्ग नहीं है धागा सुरक्षित और निम्न क्रम भी संभव है:

  1. थ्रेड एक सेट 5 पर
  2. थ्रेड बी मूल्य 7. के लिए मूल्य सेट
  3. थ्रेड सी पढ़ता 7.
  4. थ्रेड डी पढ़ता 5.
  5. थ्रेड सी धागा सुरक्षित पढ़ता 7.
  6. थ्रेड डी पढ़ता 5.
  7. ...

हूँ मैं अस्थिर साथ कि MyIntegerHolder संभालने में सही है न?

public class MyIntegerHolder { 

    private AtomicInteger atomicInteger = new AtomicInteger(); 

    public Integer getValue() { 
     return atomicInteger.get(); 
    } 

    public void setValue(Integer value) { 
     atomicInteger.set(value); 
    } 

} 

:

यह AtomicInteger का उपयोग करके एक धागा सुरक्षित पूर्णांक धारक बनाने के लिए संभव है?

यहाँ जावा संगामिति का एक टुकड़ा अभ्यास पुस्तक में है:

"पढ़ता है और परमाणु चर राशि एक ही स्मृति अर्थ विज्ञान के रूप में अस्थिर चर का लिखते हैं।"

क्या है थ्रेड-सुरक्षित MyIntegerHolder लिखने का सबसे अच्छा (अधिमानतः गैर-अवरुद्ध) तरीका?

यदि आपको उत्तर पता है, तो मैं जानना चाहता हूं कि आपको क्यों लगता है कि यह सही है। क्या यह विनिर्देशन का पालन करता है? यदि हां, तो कैसे?

+0

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

+0

@ सेबी थ्रेड-सेफ एक ऐसा प्रोग्राम है जो दो या दो से अधिक धागे में चलता है, बिना थ्रेड के किसी दूसरे पर हस्तक्षेप करता है। आप इस समस्या को हल कर सकते हैं, एक थ्रेड दूसरे तरीकों से हस्तक्षेप कर सकता है, विभिन्न तरीकों/तकनीकों में। एक साधारण int x = x + x परमाणु प्रतीत होता है, लेकिन यह नहीं है (इसके लिए अधिक, अस्थिर और परमाणु की खोज करें)! "इसे और अधिक धागा सुरक्षित बनाने के लिए", समस्या को हल करने का एक तरीका है। इससे अधिक थ्रेड-सुरक्षित नहीं होता है, एक ही समस्या को हल करने के लिए केवल अलग-अलग कार्यान्वयन है। –

+0

@ थुफिरहावत मुझे पता है कि थ्रेड-सुरक्षित प्रोग्राम क्या है, या उस जोड़ में लिखने से पहले कई पढ़ना शामिल है, लेकिन मुझे यकीन है कि इस वर्ग के बारे में कुछ नहीं है। मैं समझ नहीं पा रहा हूं कि एक पूर्णांक के लिए गेटर और सेटर को उजागर करने वाली कक्षा का उपयोग बाहरी रूप से बंद किए बिना थ्रेड सुरक्षा की आवश्यकता के लिए कुछ भी करने के लिए किया जा सकता है (जहां 'इंटीजर' पर्याप्त होगा)। – Sebi

उत्तर

-1

प्रश्न मेरे लिए आसान नहीं था, क्योंकि मैंने सोचा था (गलत तरीके से) के बारे में सब कुछ जानने से पहले संबंध से पहले जावा मेमोरी मॉडल की पूरी समझ मिलती है - और अस्थिर के अर्थशास्त्र। "JSR-133: JavaTM Memory Model and Thread Specification"

ऊपर दस्तावेज़ के सबसे अधिक प्रासंगिक टुकड़ा अनुभाग "7.3 अच्छी तरह से गठित सज़ाएँ" है:

मैं इस दस्तावेज़ में सबसे अच्छा विवरण मिल गया।

जावा मेमोरी मॉडल गारंटी देता है कि एक प्रोग्राम के सभी निष्पादन अच्छी तरह से गठित हैं। एक निष्पादन अच्छी तरह से गठित केवल अगर यह

  • का अनुसरण करता है होता है-पहले स्थिरता
  • का अनुसरण करता है तुल्यकालन-आदेश स्थिरता
  • है ... (कुछ अन्य शर्तों भी सच होना चाहिए)

होता है-पहले स्थिरता कार्यक्रम व्यवहार के बारे में एक निष्कर्ष पर आने के आमतौर पर पर्याप्त है - लेकिन नहीं इस मामले में, क्योंकि एक अस्थिर लिखने नहीं होता है - एक और अस्थिर लेखन से पहले।

अस्थिर साथ MyIntegerHolder धागा सुरक्षित है, लेकिन यह सुरक्षा तुल्यकालन-आदेश स्थिरता से आता है।

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

1

यदि आपको केवल एक चर पर प्राप्त/सेट करने की आवश्यकता है तो यह आपके जैसा अस्थिर घोषित करने के लिए पर्याप्त है। यदि आप इसे कैसे AtomicInteger सेट/करने के काम आप एक ही कार्यान्वयन

private volatile int value; 
... 

public final int get() { 
    return value; 
} 

public final void set(int newValue) { 
    value = newValue; 
} 

देखेंगे, लेकिन आप एक अस्थिर क्षेत्र को नहीं बढ़ाया जा सकता है atomically इस सरल। यह वह जगह है जहां हम AtomicInteger.incrementAndGet या getAndIncrement विधियों का उपयोग करते हैं।

4

कीवर्ड synchronized कह रहा है कि यदि Thread A and Thread BInteger तक पहुंच बनाना चाहते हैं, तो वे एक साथ ऐसा नहीं कर सकते हैं। ए बी को तब तक इंतजार कर रहा है जब तक कि मैं इसके साथ नहीं कर लेता।

दूसरी ओर, volatile धागे को और अधिक "दोस्ताना" बनाता है। वे एक-दूसरे से बात करना शुरू करते हैं और कार्यों को करने के लिए मिलकर काम करते हैं। तो जब बी पहुंचने का प्रयास करता है, तो ए उस क्षण तक बी को सूचित करेगा जो उसने उस पल तक किया है। बी अब परिवर्तनों से अवगत है और इसकी नौकरी जारी रख सकती है जहां से ए छोड़ा गया है।

जावा में, आपके पास Atomic इस कारण से है, जो कवर के तहत volatile कीवर्ड का उपयोग करते हैं, इसलिए वे बहुत कुछ कर रहे हैं, लेकिन वे आपको समय और प्रयास बचाते हैं।

जो चीज़ आप खोज रहे हैं वह AtomicInteger है, आप इस बारे में सही हैं। ऑपरेशन के लिए आप इसे करने की कोशिश कर रहे हैं यह सबसे अच्छा विकल्प है।

There are two main uses of `AtomicInteger`: 

* As an atomic counter (incrementAndGet(), etc) that can be used by many threads concurrently 

* As a primitive that supports compare-and-swap instruction (compareAndSet()) to implement non-blocking algorithms. 

एक सामान्य टिप्पणी

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

अपने बुकमार्क के कुछ:

Concurrency tips

Core Java Concurrency

Java concurrency

अद्यतन

जावा संगामिति विशिष्ट से ication उपलब्ध here:

पैकेज java.util.concurrent.atomic

वर्गों है कि एकल चर पर ताला मुक्त धागा सुरक्षित प्रोग्रामिंग का समर्थन का एक छोटा सा टूलकिट।

Instances of classes `AtomicBoolean`, `AtomicInteger`, `AtomicLong`, and `AtomicReference` each provide access and updates to a single variable of the corresponding type. 
Each class also provides appropriate utility methods for that type. 
For example, classes `AtomicLong` and AtomicInteger provide atomic increment methods. 

The memory effects for accesses and updates of atomics generally follow the rules for volatiles: 

get has the memory effects of reading a volatile variable. 
set has the memory effects of writing (assigning) a volatile variable. 

इसके अलावाHere

से जावा प्रोग्रामिंग भाषा volatile कीवर्ड:

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

+0

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

+0

"ए उस क्षण तक किए गए सब कुछ के बी को सूचित करेगा" - क्या आप निश्चित हैं? क्या यह विनिर्देशन का पालन करता है? पहले से होने वाले संबंध की परिभाषा में आप देख सकते हैं कि लिखने से पहले लिखना होता है, लेकिन मुझे यह खंड नहीं दिख रहा था कि लिखना होता है-दूसरे लिखने से पहले। –

+0

मुझे पता है कि परमाणु इंटेगर अस्थिर इंटीजर के समान है।लेकिन प्रश्न वास्तव में [जावा भाषा विशिष्टता] में परिभाषित संबंधों की उचित समझ के बारे में है (http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html# jls-17.4.5) –

0

जावा भाषा विशिष्टता के अध्याय 17 मेमोरी ऑपरेशंस पर होने वाले पहले संबंधों को परिभाषित करता है जैसे साझा चर के पढ़ने और लिखना। एक थ्रेड द्वारा लिखे जाने वाले परिणामों को पढ़ने के ऑपरेशन से पहले लिखने के ऑपरेशन होने पर ही किसी अन्य धागे द्वारा पढ़ने के लिए दृश्यमान होने की गारंटी दी जाती है।

  1. सिंक्रनाइज़ और वाष्पशील निर्माणों, साथ ही Thread.start() और Thread.join() विधियों, फार्म कर सकते हैं क्या होता है-पहले संबंधों। विशेष रूप से: थ्रेड में प्रत्येक क्रिया होती है- उस थ्रेड में प्रत्येक क्रिया जो प्रोग्राम के क्रम में बाद में आती है।
  2. एक मॉनिटर का एक अनलॉक (सिंक्रनाइज़ ब्लॉक या विधि निकास) होता है-पहले हर बाद ताला (सिंक्रनाइज़ ब्लॉक या विधि प्रवेश) है कि एक ही मॉनिटर की। और क्योंकि होता है-पहले संबंध सकर्मक है, अनलॉक होने से पहले सभी कार्यों में किसी भी धागा के बाद ताला लगा कि पर नजर रखने के लिए पहले एक धागे के सभी कार्यों।
  3. एक अस्थिर क्षेत्र के लिए एक लिखने होता है-पहले कि एक ही क्षेत्र के हर बाद पढ़ा। लिखते हैं और अस्थिर क्षेत्रों में पढ़ता में प्रवेश करने और बाहर निकलने पर नज़र रखता है के रूप में समान स्मृति स्थिरता प्रभाव है, लेकिन पारस्परिक अपवर्जन ताला आवश्यक नहीं है।
  4. एक कॉल शुरू करने के लिए पर एक धागा होता है-पहले शुरू धागे में किसी भी कार्रवाई की।
  5. एक धागा के सभी कार्रवाइयां तब-से पहले किसी अन्य धागा सफलतापूर्वक कि धागे पर एक में शामिल होने से वापस आती है।

संदर्भ: http://developer.android.com/reference/java/util/concurrent/package-summary.html

मेरी समझ 3 साधन से: यदि आप लिखना (नहीं आधारित पढ़ने परिणाम)/पढ़ा ठीक है। यदि आप लिखते हैं (पढ़ने के परिणाम के आधार पर, उदाहरण के लिए, वृद्धि)/पढ़ना ठीक नहीं है। चूंकि अस्थिर "आपसी बहिष्करण लॉकिंग को लागू नहीं करते हैं"

0

अस्थिरता वाला आपका MyIntegerHolder थ्रेड सुरक्षित है। लेकिन यदि आप समवर्ती कार्यक्रम कर रहे हैं, तो परमाणु इंटेगर को प्राथमिकता दी जाती है, क्योंकि यह बहुत से परमाणु संचालन भी प्रदान करता है।

पर विचार करें घटनाओं के निम्न क्रम:

  1. थ्रेड एक 5 पर
  2. थ्रेड बी मूल्य सेट 7.
  3. थ्रेड सेल्सियस के लिए मूल्य सेट मूल्य पढ़ता है।

यह जावा भाषा विशिष्टता से इस प्रकार है कि

  • "1" होता है-पहले "3"
  • "2" होता है-पहले "3"

लेकिन मैं डॉन यह नहीं देखता कि यह विनिर्देश से कैसे पालन कर सकता है कि "1" "2" से पहले होता है, इसलिए मुझे संदेह है कि "1" "2" से पहले नहीं होता है।

मुझे लगता है धागा सी पढ़ सकते हैं 7 या 5. मैं अस्थिर कीवर्ड के साथ वर्ग लगता है नहीं थ्रेड-सुरक्षित

आप यहीं हैं कि "1" होता है-पहले "3" और "2" होता है-इससे पहले "3"। "1" ऐसा नहीं होता है - "2" से पहले, लेकिन इसका मतलब यह नहीं है कि यह थ्रेड-सुरक्षित नहीं है। बात यह है कि आपके द्वारा प्रदान किया गया उदाहरण संदिग्ध है। यदि आप कह रहे हैं "मान को 5 सेट करता है", "मान को 7 पर सेट करता है", "मान पढ़ता है" अनुक्रमिक रूप से होता है, तो आप हमेशा 7 के मान को पढ़ सकते हैं और उन्हें अलग-अलग धागे में रखने के लिए बकवास है। लेकिन यदि आप कह रहे हैं कि अनुक्रम के बिना 3 धागे एक साथ निष्पादित होते हैं, तो आप 0 का मान भी प्राप्त कर सकते हैं, क्योंकि "मान पढ़ता है" पहले हो सकता है। लेकिन यह थ्रेड-सुरक्षित के साथ कुछ भी नहीं है, 3 कार्यों से कोई ऑर्डर नहीं है।

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