वाष्पशील खोजशब्द किसके लिए उपयोगी है


672

आज काम पर, मैं volatileजावा में खोजशब्द भर में आया था । इससे बहुत परिचित नहीं होने के कारण, मुझे यह स्पष्टीकरण मिला:

जावा सिद्धांत और व्यवहार: अस्थिरता का प्रबंधन

उस लेख को विस्तार से देखते हुए, जिसमें आप उस कीवर्ड का उपयोग करते हैं, क्या आप कभी इसका उपयोग करते हैं या क्या आप कभी ऐसा मामला देख सकते हैं जिसमें आप इस कीवर्ड का सही तरीके से उपयोग कर सकें?

जवाबों:


742

volatileस्मृति दृश्यता के लिए शब्दार्थ है। मूल रूप से, एक volatileफ़ील्ड का मान सभी पाठकों (विशेष रूप से अन्य थ्रेड्स) पर दिखाई देता है, जब एक ऑपरेशन पूरा होता है। बिना volatile, पाठक कुछ गैर-अद्यतन मूल्य देख सकते थे।

आपके प्रश्न का उत्तर देने के लिए: हां, मैं volatileयह नियंत्रित करने के लिए एक चर का उपयोग करता हूं कि क्या कुछ कोड लूप जारी रखता है। लूप volatileमान का परीक्षण करता है और जारी रखता है यदि यह है truefalse"स्टॉप" विधि को कॉल करके स्थिति सेट की जा सकती है । लूप देखता है falseऔर बंद करता है जब यह स्टॉप विधि के पूरा होने के बाद मूल्य का परीक्षण करता है।

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

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


118
यह उत्तर सही है, लेकिन अधूरा है। यह उस महत्वपूर्ण संपत्ति को छोड़ देता है जो volatileJSR 133 में परिभाषित नए जावा मेमोरी मॉडल के साथ आई थी: जब एक थ्रेड एक volatileवैरिएबल को पढ़ता है, तो यह न केवल अंतिम मूल्य को किसी अन्य थ्रेड द्वारा लिखे गए देखता है, बल्कि अन्य सभी वैरिएबल को भी लिखता है जो volatileलेखन के समय उस दूसरे सूत्र में दिखाई दे रहे थे । इस उत्तर और इस संदर्भ को देखें ।
एडम ज़ल्कमैन

46
शुरुआती लोगों के लिए, मैं आपसे कुछ कोड (कृपया?)
हंग्री ब्लू देव

6
प्रश्न में जुड़े लेख में कोड उदाहरण हैं।
ग्रेग मैट

मुझे लगता है कि लिंक 'हेनेसी एंड पैटरसन' टूट गया है। और 'जावा मेमोरी मॉडल' का लिंक वास्तव में ओरेकल के जावा लैंग्वेज स्पेसिफिकेशन 'चैप्टर 17. थ्रेड्स एंड लॉक्स' की ओर जाता है।
क्रिस

2
@fefrei: "तुरंत" एक बोलचाल का शब्द है। बेशक, इसकी गारंटी नहीं दी जा सकती है जब न तो निष्पादन समय और न ही थ्रेड शेड्यूलिंग एल्गोरिदम, वास्तव में निर्दिष्ट हैं। किसी प्रोग्राम के लिए यह पता लगाने का एकमात्र तरीका है कि क्या वाष्पशील रीड एक विशेष वाष्पशील लेखन के बाद है, यह जाँचकर कि क्या देखा गया मूल्य अपेक्षित लिखित है।
होल्गर

177

"... वाष्पशील संशोधक गारंटी देता है कि कोई भी धागा जो एक फ़ील्ड को पढ़ता है वह हाल ही में लिखे गए मूल्य को देखेगा।" - जोश बलोच

यदि आप उपयोग करने के बारे में सोच रहे हैं volatile, तो उस पैकेज पर पढ़ें java.util.concurrentजो परमाणु व्यवहार से संबंधित है।

एक सिंगलटन पैटर्न पर विकिपीडिया पोस्ट उपयोग में अस्थिरता दिखाता है।


18
कीवर्ड volatileऔर synchronizedकीवर्ड दोनों क्यों है ?
पिकेटो

5
एक सिंगलटन पैटर्न पर विकिपीडिया लेख के बाद से बहुत कुछ बदल गया है और यह नहीं कहा है कि volatileउदाहरण किसी भी लंबे समय तक। यह एक संग्रहीत संस्करण में पाया जा सकता है ।
bskp

1
@ptkato वे दो कीवर्ड पूरी तरह से अलग-अलग उद्देश्यों के लिए काम करते हैं, इसलिए यह सवाल तुलना के रूप में ज्यादा मायने नहीं रखता है, हालांकि वे दोनों संगामिति से संबंधित हैं। यह कहने की तरह है कि " कीवर्ड voidऔर publicकीवर्ड दोनों क्यों हैं "।
डेविड डीएस

134

इसके बारे में महत्वपूर्ण बिंदु volatile:

  1. जावा में तुल्यकालन जावा कीवर्ड का उपयोग करके संभव है synchronizedऔरvolatile और ताले।
  2. जावा में, हम synchronizedपरिवर्तनशील नहीं हो सकते । synchronizedएक चर के साथ कीवर्ड का उपयोग करना अवैध है और इसके परिणामस्वरूप संकलन त्रुटि होगी। synchronizedजावा में चर का उपयोग करने के बजाय , आप जावा volatileचर का उपयोग कर सकते हैं , जो volatileमुख्य स्मृति से चर के मूल्य को पढ़ने के लिए जेवीएम थ्रेड्स को निर्देश देगा और इसे स्थानीय रूप से कैश नहीं करेगा।
  3. यदि एक चर को कई थ्रेड्स के बीच साझा नहीं किया जाता है, तो volatileकीवर्ड का उपयोग करने की कोई आवश्यकता नहीं है ।

स्रोत

उदाहरण का उपयोग volatile:

public class Singleton {
    private static volatile Singleton _instance; // volatile variable
    public static Singleton getInstance() {
        if (_instance == null) {
            synchronized (Singleton.class) {
                if (_instance == null)
                    _instance = new Singleton();
            }
        }
        return _instance;
    }
}

पहला अनुरोध आने पर हम आलसी रूप से उदाहरण बना रहे हैं।

अगर हम _instanceवैरिएबल नहीं बनाते हैं volatileतो थ्रेड जो बना रहा Singletonहै वह दूसरे थ्रेड से संवाद करने में सक्षम नहीं है। इसलिए यदि थ्रेड ए सिंगलटन उदाहरण का निर्माण कर रहा है और निर्माण के ठीक बाद, सीपीयू को नष्ट कर देता है आदि, अन्य सभी धागे मूल्य को देखने में सक्षम नहीं होंगे_instance शून्य रूप में नहीं और उन्हें विश्वास होगा कि यह अभी भी अशक्त है।

ऐसा क्यों होता है? क्योंकि पाठक थ्रेड्स कोई लॉकिंग नहीं कर रहे हैं और जब तक कि लेखक थ्रेड एक सिंक्रनाइज़ किए गए ब्लॉक से बाहर नहीं निकलता है, तब तक मेमोरी को सिंक्रनाइज़ नहीं किया जाएगा और _instanceमुख्य मेमोरी में वैल्यू अपडेट नहीं की जाएगी। जावा में वोलेटाइल कीवर्ड के साथ, यह जावा द्वारा ही संभाला जाता है और इस तरह के अपडेट सभी रीडर थ्रेड्स द्वारा दिखाई देंगे।

निष्कर्ष : volatileथ्रेड के बीच मेमोरी की सामग्री को संप्रेषित करने के लिए भी कीवर्ड का उपयोग किया जाता है।

बिना अस्थिर के उदाहरण का उपयोग:

public class Singleton{    
    private static Singleton _instance;   //without volatile variable
    public static Singleton getInstance(){   
          if(_instance == null){  
              synchronized(Singleton.class){  
               if(_instance == null) _instance = new Singleton(); 
      } 
     }   
    return _instance;  
    }

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

यहाँ छवि विवरण दर्ज करें

volatileजावा में उपयोग :

असफल-तेज़ पुनरावृत्तियों को आमतौरvolatile पर सूची ऑब्जेक्ट पर एक काउंटर का उपयोग करके लागू किया जाता है ।

  • जब सूची को अद्यतन किया जाता है, तो काउंटर को बढ़ा दिया जाता है।
  • जब एक Iteratorबनाया जाता है, तो काउंटर का वर्तमान मूल्य अंदर एम्बेडेड होता हैIterator ऑब्जेक्ट ।
  • जब एक Iteratorऑपरेशन किया जाता है, तो विधि दो काउंटर मानों की तुलना करती है और ConcurrentModificationExceptionयदि वे अलग-अलग हैं तो फेंकता है ।

असफल-सुरक्षित पुनरावृत्तियों का कार्यान्वयन आमतौर पर हल्का होता है। वे आम तौर पर विशिष्ट सूची कार्यान्वयन के डेटा संरचनाओं के गुणों पर भरोसा करते हैं। कोई सामान्य पैटर्न नहीं है।


2
"असफल-तेज़ पुनरावृत्तियों को आम तौर पर एक अस्थिर काउंटर का उपयोग करके लागू किया जाता है" - अब मामला नहीं है, बहुत महंगा है: Bugs.java.com/bugdatabase/view_bug.do?bug_id=6625725
Golovanov

क्या _instance के लिए दोहरी जाँच सुरक्षित है? मुझे लगा कि वे अस्थिर नहीं हैं
डेक्सटोर्स

"जो जेवीएम थ्रेड्स को मुख्य मेमोरी से वाष्पशील चर के मूल्य को पढ़ने का निर्देश देगा और इसे स्थानीय रूप से कैश नहीं करेगा।" अच्छी बात है
हुमायूँ अहमद

धागा-सुरक्षा के लिए एक साथ भी जा सकता है private static final Singleton _instance;
क्रिस 311

53

volatile धागे को रोकने के लिए बहुत उपयोगी है।

ऐसा नहीं है कि आपको अपने स्वयं के धागे लिखना चाहिए, जावा 1.6 में बहुत अच्छे थ्रेड पूल हैं। लेकिन अगर आपको यकीन है कि आपको एक धागा की आवश्यकता है, तो आपको यह जानना होगा कि इसे कैसे रोकें।

धागे के लिए मैं जिस पैटर्न का उपयोग करता हूं वह है:

public class Foo extends Thread {

  private volatile boolean close = false;

  public void run() {
    while(!close) {
      // do work
    }
  }
  public void close() {
    close = true;
    // interrupt here if needed
  }
}

उपरोक्त कोड सेगमेंट में, closeलूप में थ्रेड रीडिंग उस कॉल से भिन्न होती है close()। अस्थिर के बिना, लूप को चलाने वाले धागे को बंद होने के लिए परिवर्तन कभी नहीं दिखाई दे सकता है।

ध्यान दें कि सिंक्रनाइज़ेशन की कोई आवश्यकता नहीं है


2
मुझे आश्चर्य है कि ऐसा क्यों जरूरी है। क्या यह आवश्यक नहीं है कि यदि अन्य थ्रेड्स को इस थ्रेड के स्थिति परिवर्तन पर इस तरह से प्रतिक्रिया करनी है ताकि थ्रेड्स सिंक्रनाइज़ेशन खतरे में हो?
जोरी

27
@ जोरी, आपको अस्थिर चाहिए, क्योंकि लूप में बंद होने वाला थ्रेड रीडिंग क्लोज () कॉल करने वाले से अलग होता है। अस्थिर के बिना, लूप को चलाने वाले धागे को बंद होने के लिए परिवर्तन कभी नहीं दिखाई दे सकता है।
पाइरोलॉनिक

क्या आप कहेंगे कि इस तरह से एक धागा को रोकने या थ्रेड # इंटरप्ट () और थ्रेड # isInterrupted () विधियों का उपयोग करने के बीच एक फायदा है?
रिकार्डो बेलचिअर

2
@Pyrolinary - क्या आपने देखा है कि थ्रेड को कभी भी व्यवहार में परिवर्तन नहीं देखा है? या आप उस मुद्दे को मज़बूती से ट्रिगर करने के लिए उदाहरण का विस्तार कर सकते हैं? मैं उत्सुक हूं क्योंकि मुझे पता है कि मैंने कोड का उपयोग किया है (और दूसरों को देखा है) जो मूल रूप से उदाहरण के बिना volatileकीवर्ड के समान है , और यह हमेशा ठीक काम करता है।
अरथ

2
@ बरोठा: आज के जेवीएम के साथ, आप यह देख सकते हैं कि व्यवहार में, यहां तक ​​कि सबसे सरल उदाहरणों के साथ, हालांकि, आप इस व्यवहार को मज़बूती से नहीं दोहरा सकते । अधिक जटिल अनुप्रयोगों के साथ, आपके पास कभी-कभी आपके पास कोड के साथ मेमोरी दृश्यता गारंटी के साथ अन्य कार्य होते हैं जो इसे काम करने के लिए बनाते हैं, जो विशेष रूप से खतरनाक है क्योंकि आप नहीं जानते कि यह क्यों काम करता है और आपके कोड में एक सरल, स्पष्ट रूप से असंबंधित परिवर्तन आपके टूट सकता है आवेदन…
होल्गर

31

उपयोग volatileकरने के लिए एक सामान्य उदाहरण volatile booleanएक धागे को समाप्त करने के लिए ध्वज के रूप में एक चर का उपयोग करना है । यदि आपने एक धागा शुरू किया है, और आप इसे एक अलग धागे से सुरक्षित रूप से बाधित करने में सक्षम होना चाहते हैं, तो आप समय-समय पर एक ध्वज की जांच कर सकते हैं। इसे रोकने के लिए, ध्वज को सही पर सेट करें। झंडे को बनाकर volatile, आप यह सुनिश्चित कर सकते हैं कि जो थ्रेड इसकी जाँच कर रहा है वह देखेगा कि यह अगली बार सेट हो गया है, यह बिना किसी synchronizedब्लॉक का उपयोग किए इसे चेक करता है ।


27

volatileकीवर्ड के साथ घोषित एक चर में दो मुख्य गुण होते हैं जो इसे विशेष बनाते हैं।

  1. अगर हमारे पास एक अस्थिर चर है, तो इसे कंप्यूटर (माइक्रोप्रोसेसर) कैश मेमोरी में किसी भी धागे से कैश नहीं किया जा सकता है। प्रवेश हमेशा मुख्य मेमोरी से हुआ।

  2. यदि कोई लेखन ऑपरेशन एक अस्थिर चर पर चल रहा है, और अचानक एक रीड ऑपरेशन का अनुरोध किया जाता है, तो यह गारंटी दी जाती है कि रीड ऑपरेशन से पहले राइट ऑपरेशन समाप्त हो जाएगा

उपरोक्त दो गुण जो घटाते हैं

  • एक अस्थिर चर पढ़ने वाले सभी धागे निश्चित रूप से नवीनतम मूल्य पढ़ेंगे। क्योंकि कोई भी कैश्ड मान इसे प्रदूषित नहीं कर सकता है। और रीड रिक्वेस्ट को मौजूदा राइट ऑपरेशन के पूरा होने के बाद ही दिया जाएगा।

और दूसरी ओर,

  • अगर हम आगे बताए गए # 2 की जांच करते हैं , तो हम यह देख सकते हैं कि volatileकीवर्ड एक साझा चर बनाए रखने का एक आदर्श तरीका है, जिसमें रीडर थ्रेड्स की संख्या 'n' है और इसे एक्सेस करने के लिए केवल एक लेखक धागा है। एक बार जब हम volatileकीवर्ड जोड़ लेते हैं , तो यह हो जाता है। धागा सुरक्षा के बारे में कोई अन्य उपरि नहीं।

Conversly,

हम केवल एक साझा चर को संतुष्ट करने के लिए कीवर्ड का उपयोग नहीं कर सकते हैंvolatile , जिसमें एक से अधिक लेखक थ्रेड हैं


3
यह अस्थिर और सिंक्रनाइज़ के बीच का अंतर बताता है।
अजय

13

किसी ने लंबे और दोहरे चर प्रकार के लिए पढ़ने और लिखने के संचालन के उपचार का उल्लेख नहीं किया है। रीड और राइट संदर्भ चर के लिए और लंबे और दोहरे चर प्रकारों को छोड़कर अधिकांश आदिम चर के लिए परमाणु संचालन हैं, जो कि परमाणु संचालन होने के लिए अस्थिर कीवर्ड का उपयोग करना चाहिए। @संपर्क


इसे और अधिक स्पष्ट करने के लिए, बूलियन वाष्पशील सेट करने के लिए NO NEED है, क्योंकि एक बूलियन IS ALREADY परमाणु का पढ़ना और लिखना है।
काई वांग

2
@KaiWang आपको परमाणुता उद्देश्यों के लिए बूलियन पर अस्थिर का उपयोग करने की आवश्यकता नहीं है। लेकिन आप निश्चित रूप से दृश्यता कारणों से हो सकते हैं। क्या आपके कहने का मतलब है?
सुजैन

12

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


10

मेरी राय में, थ्रेड को रोकने के अलावा दो महत्वपूर्ण परिदृश्य जिसमें अस्थिर कीवर्ड का उपयोग किया जाता है:

  1. डबल-चेकिंग लॉकिंग तंत्र । अक्सर सिंगलटन डिजाइन पैटर्न में उपयोग किया जाता है। इसमें सिंगलटन ऑब्जेक्ट को अस्थिर घोषित करने की आवश्यकता है
  2. स्प्रिचुअल वेकअप । थ्रेड कभी-कभी प्रतीक्षा कॉल से जाग सकता है, भले ही कोई सूचित कॉल जारी नहीं किया गया हो। इस व्यवहार को स्परियस वेकअप कहा जाता है। यह एक सशर्त चर (बूलियन ध्वज) का उपयोग करके काउंटर किया जा सकता है। जब तक झंडा सही है तब तक प्रतीक्षा () कॉल को थोड़ी देर के लूप में रखें। इसलिए यदि नोटिफाई / NotifyAll के अलावा किसी भी कारण से थ्रेड वेट कॉल से जागता है, तो यह ध्वज का सामना अभी भी सही है और इसलिए कॉल फिर से प्रतीक्षा करते हैं। कॉल करने से पहले सूचित करें इस ध्वज को सही पर सेट करें। इस मामले में बूलियन ध्वज को अस्थिर घोषित किया जाता है

पूरा # 2 खंड बहुत भ्रमित लगता है, यह खोए हुए नोटिफिकेशन, गंभीर वेकअप और मेमोरी विजिबिलिटी के मुद्दों का सामना कर रहा है। इसके अलावा अगर ध्वज के सभी उपयोग सिंक्रनाइज़ हैं तो अस्थिर अतिरेक है। मुझे लगता है कि मुझे आपकी बात समझ में आ गई है, लेकिन सही मायने में जागना सही नहीं है। कृपया स्पष्ट करें।
नाथन ह्यूजेस

5

यदि आप एक मल्टीथ्रेड एप्लिकेशन विकसित कर रहे हैं, तो आपको 'वाष्पशील' कीवर्ड, या 'सिंक्रनाइज़' और किसी भी अन्य संगामिति नियंत्रण उपकरण और तकनीकों का उपयोग करने की आवश्यकता होगी। ऐसे एप्लिकेशन का उदाहरण डेस्कटॉप ऐप्स हैं।

यदि आप एक ऐसा एप्लिकेशन विकसित कर रहे हैं जो एप्लिकेशन सर्वर (Tomcat, JBoss AS, Glassfish, आदि) पर तैनात किया जाएगा, तो आपको अपने आप को नियंत्रण संगोष्ठी को नियंत्रित करने की आवश्यकता नहीं है क्योंकि यह पहले से ही एप्लिकेशन सर्वर द्वारा संबोधित है। वास्तव में, अगर मुझे सही ढंग से याद है कि जावा ईई मानक सर्वलेट्स और ईजेबी में किसी भी संगामिति नियंत्रण को प्रतिबंधित करता है, क्योंकि यह 'अवसंरचना' परत का हिस्सा है जिसे आपको इसे संभालने से मुक्त किया जाना चाहिए था। यदि आप सिंगलटन ऑब्जेक्ट्स को कार्यान्वित कर रहे हैं तो आप केवल इस तरह के ऐप में ही कॉनक्युलर कंट्रोल करते हैं। यह पहले से ही संबोधित है अगर आप स्प्रिंग की तरह रूपरेखा का उपयोग करके अपने घटकों को बुनते हैं।

तो, जावा विकास के अधिकांश मामलों में जहां एप्लिकेशन एक वेब अनुप्रयोग है और वसंत या ईजेबी जैसे आईओसी ढांचे का उपयोग करके, आपको 'अस्थिर' का उपयोग करने की आवश्यकता नहीं होगी।


5

volatileकेवल गारंटी देता है कि सभी धागे, यहां तक ​​कि खुद भी, वृद्धि कर रहे हैं। उदाहरण के लिए: एक काउंटर एक ही समय में चर का एक ही चेहरा देखता है। इसका उपयोग सिंक्रनाइज़ या परमाणु या अन्य सामान के बजाय नहीं किया जाता है, यह रीड्स को पूरी तरह से सिंक्रनाइज़ करता है। कृपया इसकी तुलना अन्य जावा कीवर्ड से न करें। जैसा कि उदाहरण से पता चलता है कि अस्थिर परिवर्तनशील संचालन भी परमाणु होते हैं वे एक बार में विफल या सफल होते हैं।

package io.netty.example.telnet;

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static volatile  int a = 0;
    public static void main(String args[]) throws InterruptedException{

        List<Thread> list = new  ArrayList<Thread>();
        for(int i = 0 ; i<11 ;i++){
            list.add(new Pojo());
        }

        for (Thread thread : list) {
            thread.start();
        }

        Thread.sleep(20000);
        System.out.println(a);
    }
}
class Pojo extends Thread{
    int a = 10001;
    public void run() {
        while(a-->0){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Main.a++;
            System.out.println("a = "+Main.a);
        }
    }
}

यहां तक ​​कि आप अस्थिर हैं या नहीं परिणाम हमेशा अलग होंगे। लेकिन अगर आप AtomicInteger का उपयोग करते हैं तो नीचे दिए गए परिणाम हमेशा समान होंगे। सिंक्रनाइज़ के साथ भी यही है।

    package io.netty.example.telnet;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;

    public class Main {

        public static volatile  AtomicInteger a = new AtomicInteger(0);
        public static void main(String args[]) throws InterruptedException{

            List<Thread> list = new  ArrayList<Thread>();
            for(int i = 0 ; i<11 ;i++){
                list.add(new Pojo());
            }

            for (Thread thread : list) {
                thread.start();
            }

            Thread.sleep(20000);
            System.out.println(a.get());

        }
    }
    class Pojo extends Thread{
        int a = 10001;
        public void run() {
            while(a-->0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Main.a.incrementAndGet();
                System.out.println("a = "+Main.a);
            }
        }
    }

4

हां, मैं इसे काफी उपयोग करता हूं - यह बहु-थ्रेडेड कोड के लिए बहुत उपयोगी हो सकता है। आपके द्वारा बताया गया लेख अच्छा है। हालांकि दो महत्वपूर्ण बातों को ध्यान में रखना है:

  1. आपको केवल अस्थिर का उपयोग करना चाहिए यदि आप पूरी तरह से समझते हैं कि यह क्या करता है और यह सिंक्रनाइज़ करने के लिए कैसे भिन्न होता है। कई स्थितियों में अस्थिरता सतह पर दिखाई देती है, सिंक्रनाइज़ करने के लिए अधिक सरल विकल्प के रूप में, जब अक्सर अस्थिर की बेहतर समझ यह स्पष्ट करती है कि सिंक्रनाइज़ एकमात्र विकल्प है जो काम करेगा।
  2. वाष्पशील वास्तव में पुराने जेवीएम के बहुत से काम नहीं करता है, हालांकि सिंक्रनाइज़ करता है। मुझे याद है कि एक दस्तावेज जिसे विभिन्न JVM में समर्थन के विभिन्न स्तरों का संदर्भ दिया गया था, लेकिन दुर्भाग्य से मैं इसे अब नहीं पा सकता हूं। निश्चित रूप से इसे देखें अगर आप जावा प्री 1.5 का उपयोग कर रहे हैं या यदि आपका जेवीएम पर नियंत्रण नहीं है तो आपका प्रोग्राम चालू रहेगा।

4

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

केवल सदस्य चर अस्थिर या क्षणिक हो सकते हैं।


3

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


3

वाष्पशील कीवर्ड के दो अलग-अलग उपयोग हैं।

  1. रजिस्टर से मान पढ़ने से जेवीएम को रोकता है (कैश के रूप में मान लें), और इसके मूल्य को मेमोरी से पढ़ने के लिए मजबूर करता है।
  2. मेमोरी-इन-संगति त्रुटियों के जोखिम को कम करता है।

जेवीएम को रजिस्टर में पढ़ने के मूल्यों से बचाता है, और इसके मूल्य को मेमोरी से पढ़ने के लिए मजबूर करता है।

एक व्यस्त ध्वज का उपयोग एक थ्रेड को जारी रखने के लिए किया जाता है जबकि डिवाइस व्यस्त रहता है और ध्वज को लॉक द्वारा संरक्षित नहीं किया जाता है:

while (busy) {
    /* do something else */
}

टेस्टिंग थ्रेड जारी रहेगा जब कोई अन्य बिजी फ़्लैग को बंद करता है :

busy = 0;

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

स्मृति संगतता त्रुटियों के जोखिम को कम करता है।

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

मेमोरी स्थिरता त्रुटियों के बिना पढ़ने, लिखने की तकनीक को परमाणु कार्रवाई कहा जाता है

परमाणु क्रिया वह है जो प्रभावी रूप से एक ही बार में होती है। एक परमाणु क्रिया बीच में नहीं रुक सकती: यह या तो पूरी तरह से होता है, या यह बिल्कुल भी नहीं होता है। जब तक क्रिया पूर्ण नहीं होती तब तक किसी परमाणु क्रिया का कोई भी दुष्प्रभाव दिखाई नहीं देता है।

नीचे ऐसी क्रियाएं हैं जो आप बता सकते हैं कि परमाणु हैं:

  • पढ़ता है और लिखता है संदर्भ चर के लिए और सबसे आदिम चर (लंबे और दोहरे को छोड़कर सभी प्रकार) के लिए परमाणु हैं।
  • वाष्पशील (लंबे और दोहरे चर सहित) घोषित किए गए सभी चरों के लिए प्रतिक्रियाएं और लिखना परमाणु हैं ।

चीयर्स!


3

volatileएक प्रोग्रामर के लिए कहता है कि मूल्य हमेशा अद्यतित रहेगा। समस्या यह है कि मूल्य को विभिन्न प्रकार की हार्डवेयर मेमोरी पर सहेजा जा सकता है। उदाहरण के लिए यह सीपीयू रजिस्टर, सीपीयू कैश, रैम हो सकता है ... СPU रजिस्टर और सीपीयू कैश सीपीयू से संबंधित है और रैम के विपरीत एक डेटा साझा नहीं कर सकता है जो मल्टीथ्रेडिंग एनविप्रोममेंट में बचाव पर है

यहाँ छवि विवरण दर्ज करें

volatileकीवर्ड का कहना है कि एक चर को सीधे / रैम मेमोरी से पढ़ा और लिखा जाएगा । इसमें कुछ संगणना पदचिह्न हैं

Java 5[के बारे में] काvolatile समर्थन करके बढ़ायाhappens-before

उस क्षेत्र के प्रत्येक बाद के पढ़ने से पहले एक अस्थिर क्षेत्र के लिए एक लेखन होता है।

volatileकीवर्ड उस स्थिति को ठीक नहीं करताrace condition है जब कई धागे एक साथ कुछ मान लिख सकते हैं। उत्तर synchronizedकीवर्ड [के बारे में] है

परिणामस्वरूप यह सुरक्षा तभी होती है जब एक धागा लिखता है और अन्य केवल volatileमूल्य पढ़ते हैं

अस्थिर बनाम सिंक्रनाइज़


2

वाष्पशील निम्नलिखित करता है।

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

पूरी तरह से चलने वाले कोड का पालन करने से पता चलता है कि सिंक्रनाइज़ किए गए कीवर्ड का उपयोग किए बिना कितने थ्रेड पूर्वनिर्धारित क्रम और प्रिंट आउटपुट में निष्पादित कर सकते हैं।

thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3
thread 0 prints 0
thread 1 prints 1
thread 2 prints 2
thread 3 prints 3

इसे प्राप्त करने के लिए हम निम्नलिखित पूर्ण विकसित कोड का उपयोग कर सकते हैं।

public class Solution {
    static volatile int counter = 0;
    static int print = 0;
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Thread[] ths = new Thread[4];
        for (int i = 0; i < ths.length; i++) {
            ths[i] = new Thread(new MyRunnable(i, ths.length));
            ths[i].start();
        }
    }
    static class MyRunnable implements Runnable {
        final int thID;
        final int total;
        public MyRunnable(int id, int total) {
            thID = id;
            this.total = total;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (true) {
                if (thID == counter) {
                    System.out.println("thread " + thID + " prints " + print);
                    print++;
                    if (print == total)
                        print = 0;
                    counter++;
                    if (counter == total)
                        counter = 0;
                } else {
                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        // log it
                    }
                }
            }
        }
    }
}

निम्नलिखित गीथब लिंक में एक रीडमी है, जो उचित स्पष्टीकरण देता है। https://github.com/sankar4git/volatile_thread_ordering


1

ओरेकल डॉक्यूमेंटेशन पेज से , मेमोरी कंसिस्टेंसी मुद्दों को ठीक करने के लिए अस्थिर चर की आवश्यकता उत्पन्न होती है:

वाष्पशील चरों का उपयोग करने से मेमोरी की संगति त्रुटियों का खतरा कम हो जाता है, क्योंकि वाष्पशील चर के लिए कोई भी लिखता है, उसी चर के बाद के पठन के साथ संबंध होने से पहले होता है।

इसका मतलब है कि एक परिवर्तनशील परिवर्तन volatileहमेशा अन्य थ्रेड्स को दिखाई देता है। इसका अर्थ यह भी है कि जब कोई थ्रेड एक अस्थिर चर को पढ़ता है, तो यह न केवल नवीनतम परिवर्तन को देखता है volatile, बल्कि कोड के साइड इफेक्ट्स भी होते हैं जो परिवर्तन का नेतृत्व करते हैं।

जैसा कि Peter Parkerउत्तर में समझाया गया है, volatileसंशोधक की अनुपस्थिति में , प्रत्येक थ्रेड के स्टैक में चर की अपनी प्रति हो सकती है। जैसा कि वैरिएबल बनाकरvolatile , स्मृति संगतता मुद्दों को तय किया गया है।

बेहतर समझ के लिए जेनकोव ट्यूटोरियल पेज पर एक नज़र डालें ।

अस्थिर और उपयोग के मामलों के बारे में अधिक जानकारी के लिए संबंधित SE प्रश्न पर एक नज़र डालें:

जावा में अस्थिर और सिंक्रनाइज़ के बीच अंतर

एक व्यावहारिक उपयोग मामला:

आपके पास कई धागे हैं, जिन्हें वर्तमान समय में एक विशेष प्रारूप में उदाहरण के लिए प्रिंट करने की आवश्यकता है java.text.SimpleDateFormat("HH-mm-ss"):। योन में एक वर्ग हो सकता है, जो वर्तमान समय को परिवर्तित करता है SimpleDateFormatऔर हर एक सेकंड के लिए चर को अद्यतन करता है। अन्य सभी धागे लॉग फ़ाइलों में वर्तमान समय को प्रिंट करने के लिए बस इस अस्थिर चर का उपयोग कर सकते हैं।


1

वाष्पशील चर प्रकाश-भार तुल्यकालन हैं। जब सभी थ्रेड्स के बीच नवीनतम डेटा की दृश्यता की आवश्यकता होती है और परमाणुता से समझौता किया जा सकता है, तो ऐसी स्थितियों में अस्थिर वेरिएबल्स को प्राथमिकता दी जानी चाहिए। वाष्पशील चर पर पढ़ें हमेशा किसी भी थ्रेड द्वारा किए गए सबसे हाल के लेखन को वापस करें क्योंकि वे न तो रजिस्टरों में कैश होते हैं और न ही कैश में जहां अन्य प्रोसेसर नहीं देख सकते हैं। वाष्पशील लॉक-फ्री है। मैं अस्थिर का उपयोग करता हूं, जब परिदृश्य ऊपर वर्णित मानदंडों को पूरा करता है।


-1

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

यदि परिवर्तन 1 थ्रेड द्वारा किए गए हैं और अन्य को केवल इस मान को पढ़ने की आवश्यकता है, तो वाष्पशील उपयुक्त होगा।


-1

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


-2

नीचे volatileचर के लिए आवश्यकता को प्रदर्शित करने के लिए एक बहुत ही सरल कोड है जो थ्रेड निष्पादन को अन्य थ्रेड से नियंत्रित करने के लिए उपयोग किया जाता है (यह एक ऐसा परिदृश्य है जहां volatileआवश्यक है)।

// Code to prove importance of 'volatile' when state of one thread is being mutated from another thread.
// Try running this class with and without 'volatile' for 'state' property of Task class.
public class VolatileTest {
    public static void main(String[] a) throws Exception {
        Task task = new Task();
        new Thread(task).start();

        Thread.sleep(500);
        long stoppedOn = System.nanoTime();

        task.stop(); // -----> do this to stop the thread

        System.out.println("Stopping on: " + stoppedOn);
    }
}

class Task implements Runnable {
    // Try running with and without 'volatile' here
    private volatile boolean state = true;
    private int i = 0;

    public void stop() {
        state = false;
    } 

    @Override
    public void run() {
        while(state) {
            i++;
        }
        System.out.println(i + "> Stopped on: " + System.nanoTime());
    }
}

जब volatileउपयोग नहीं किया जाता है: आप ' स्टॉपिंग: एक्सएक्सएक्स ' : ' स्टॉप ऑन: एक्सएक्सएक्स ' के बाद भी ' स्टॉप ऑन: एक्सएक्सएक्स ' संदेश कभी नहीं देख पाएंगे और यह कार्यक्रम चलता रहता है।

Stopping on: 1895303906650500

जब volatileउपयोग किया जाता है: आप तुरंत ' स्टॉप ऑन: xxx ' देखेंगे ।

Stopping on: 1895285647980000
324565439> Stopped on: 1895285648087300

डेमो: https://repl.it/repls/SilverAgonizingObjectcode


Downvoter करने के लिए: व्याख्या करने के लिए क्यों downvote? अगर यह सच नहीं है, तो कम से कम मैं गलत सीखूंगा। मैंने एक ही टिप्पणी को दो बार जोड़ा है, लेकिन यह नहीं जानता कि कौन बार-बार हटा रहा है
मणिकंता

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