2012-06-12 20 views
14

क्या हमें निजी फ़ील्ड को volatile के रूप में घोषित करना चाहिए यदि इंस्टेंट का उपयोग एकाधिक थ्रेड में किया जाता है?जावा: गेटर्स और सेटर्स के साथ 'अस्थिर' निजी फ़ील्ड

import java.util.concurrent.TimeUnit; 

// Broken! - How long would you expect this program to run? 
public class StopThread { 
    private static boolean stopRequested; // works, if volatile is here 

    public static void main(String[] args) throws InterruptedException { 
     Thread backgroundThread = new Thread(new Runnable() { 
      public void run() { 
       int i = 0; 
       while (!stopRequested) 
        i++; 
      } 
     }); 
     backgroundThread.start(); 
     TimeUnit.SECONDS.sleep(1); 
     stopRequested = true; 
    } 
} 

स्पष्टीकरण का कहना है कि

while(!stopRequested) 
    i++; 

कुछ इस तरह के लिए अनुकूलित है:

if(!stopRequested) 
    while(true) 
     i++; 

Effective Java में, वहाँ एक उदाहरण है जहां कोड अस्थिर बिना काम नहीं करता है

तो stopRequested के और संशोधनों को नहीं देखा गया है पृष्ठभूमि धागे से, तो यह हमेशा के लिए loops। (Btw, कि कोड JRE7 पर volatile बिना समाप्त हो जाता है।)

अब इस वर्ग पर विचार करें: इस प्रकार

public class Bean { 
    private boolean field = true; 

    public boolean getField() { 
     return field; 
    } 

    public void setField(boolean value) { 
     field = value; 
    } 
} 

और एक धागा:

public class Worker implements Runnable { 
    private Bean b; 

    public Worker(Bean b) { 
     this.b = b; 
    } 

    @Override 
    public void run() { 
     while(b.getField()) { 
      System.err.println("Waiting..."); 
      try { Thread.sleep(1000); } 
      catch(InterruptedException ie) { return; } 
     } 
    } 
} 

ऊपर कोड के रूप में वाष्पशील का उपयोग किए बिना की उम्मीद काम करता है :

public class VolatileTest { 
    public static void main(String [] args) throws Exception { 
     Bean b = new Bean(); 

     Thread t = new Thread(new Worker(b)); 
     t.start(); 
     Thread.sleep(3000); 

     b.setField(false); // stops the child thread 
     System.err.println("Waiting the child thread to quit"); 
     t.join(); 
     // if the code gets, here the child thread is stopped 
     // and it really gets, with JRE7, 6 with -server, -client 
    } 
} 

मुझे लगता है कि सार्वजनिक सेटटर के कारण, सी ompiler/JVM को कभी भी कोड को अनुकूलित नहीं करना चाहिए जो getField() पर कॉल करता है, लेकिन this article कहता है कि कुछ "अस्थिर बीन" पैटर्न (पैटर्न # 4) है, जिसे उत्परिवर्तनीय थ्रेड-सुरक्षित कक्षाएं बनाने के लिए लागू किया जाना चाहिए। अद्यतन: शायद यह आलेख केवल आईबीएम जेवीएम के लिए लागू होता है?

सवाल यह है कि: जेएलएस का कौन सा हिस्सा स्पष्ट रूप से या स्पष्ट रूप से कहता है कि सार्वजनिक गेटर्स/सेटर्स के साथ निजी आदिम फ़ील्ड volatile (या उन्हें नहीं) के रूप में घोषित किया जाना चाहिए?

लंबे प्रश्न के लिए खेद है, मैंने विवरण में समस्या की व्याख्या करने की कोशिश की। अगर कुछ स्पष्ट नहीं है तो मुझे बताएं। धन्यवाद।

+0

ऐसा नहीं है कि आपको थ्रेड को रद्द करने के लिए उस फ़ील्ड की आवश्यकता है, तो आप थ्रेड पर बाधित ध्वज का उपयोग कर सकते हैं। –

+1

@NathanHughes, ये कक्षाएं केवल न्यूनतम उदाहरण हैं, वास्तविक कोड अलग है, वहां कोई थ्रेड बाधा नहीं है। – khachik

उत्तर

4

इससे पहले कि मैं अपने प्रश्न का उत्तर मैं BTW

पता करने के लिए चाहते हैं, कि कोड JRE7

पर अस्थिर बिना समाप्त हो जाता है

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

अपने प्रश्न का उत्तर देने इसलिए

@Override 
public void run() { 
    if(b.getField()){ 
     while(true) { 
      System.err.println("Waiting..."); 
      try { Thread.sleep(1000); } 
      catch(InterruptedException ie) { return; } 
     } 
    } 
} 

यह अभी भी क्रमिक रूप से संगत है और इस तरह जावा के गारंटी देता है का कहना है - आप विशेष रूप से 17.4.3 पढ़ सकते हैं:

प्रत्येक थ्रेड टी द्वारा किए गए सभी इंटर-थ्रेड कार्यों में से टी का प्रोग्राम ऑर्डर कुल ऑर्डर है जो ऑर्डर को दर्शाता है जिसमें इन क्रियाओं को इंट्रा-थ्रेड टी के अर्थशास्त्र के अनुसार किया जाएगा।

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

दूसरे शब्दों में - जब तक एक धागा संकलक/स्मृति आदेश देने यह क्रमिक रूप से सुसंगत माना जाता है फिर से की परवाह किए बिना एक ही क्रम में पढ़ा है और एक क्षेत्र के लिखने देखेंगे।

9

सवाल यह है: JLS का कौन सा हिस्सा स्पष्ट या परोक्ष का कहना है कि सार्वजनिक getters/setters के साथ निजी आदिम क्षेत्रों (या वे की जरूरत नहीं है) अस्थिर रूप में घोषित किया जाना चाहिए?

जेएलएस मेमोरी मॉडल गेटर्स/सेटर्स के बारे में परवाह नहीं करता है। वे मेमोरी मॉडल परिप्रेक्ष्य से नो-ऑप्स हैं - आप सार्वजनिक क्षेत्रों तक पहुंच सकते हैं। एक विधि कॉल के पीछे बूलियन लपेटना इसकी स्मृति दृश्यता को प्रभावित नहीं करता है। आपका उत्तरार्द्ध उदाहरण भाग्य से पूरी तरह से काम करता है।

क्या हमें निजी क्षेत्रों को अस्थिर के रूप में घोषित करना चाहिए यदि इंस्टेंट का उपयोग कई धागे में किया जाता है?

एक वर्ग (सेम) बहु-क्रम वातावरण में प्रयोग की जाने वाली है, तो आप किसी भी तरह कि रखना चाहिए खाते में। निजी क्षेत्र बनाना volatile एक दृष्टिकोण है: यह सुनिश्चित करता है कि प्रत्येक थ्रेड को उस क्षेत्र के नवीनतम मूल्य को देखने की गारंटी दी जाती है, न कि किसी भी प्रकार के कैश/अनुकूलित मूल्यों को अनुकूलित किया जाता है। लेकिन यह atomicity की समस्या का समाधान नहीं करता है।

The article you linked to किसी भी जेवीएम पर लागू होता है जो जेवीएम विनिर्देश (जो जेएलएस पर निर्भर करता है) का पालन करता है। आपको जेवीएम विक्रेता, संस्करण, झंडे, कंप्यूटर और ओएस के आधार पर विभिन्न परिणाम मिलेंगे, प्रोग्राम चलाने के समय (हॉटस्पॉट ऑप्टिमाइज़ेशन अक्सर 10000 वें रन के बाद लातें) आदि, इसलिए आपको वास्तव में स्पेक को समझना चाहिए और ध्यान से पालन करना चाहिए विश्वसनीय कार्यक्रम बनाने के लिए नियमों के लिए। इस मामले में प्रयोग करना यह पता लगाने का एक खराब तरीका है कि चीजें कैसे काम करती हैं क्योंकि JVM किसी भी तरह से व्यवहार कर सकता है जब तक कि यह spec के भीतर आता है, और अधिकांश JVMs में सभी तरह के गतिशील अनुकूलन का भार होता है।

+0

धन्यवाद। एक बूलियन वैरिएबल सेट करना (या लंबे/डबल को छोड़कर कोई अन्य आदिम चर) एक परमाणु ऑपरेशन है। मेरा सवाल यह है कि जेएसएल इस मामले में कोड व्यवहार को कैसे परिभाषित करता है। या यह कैसे परिभाषित नहीं करता है, अगर यह स्पष्ट है कि मेरा क्या मतलब है। – khachik

+0

जेएलएस आपके मामले में विशेष रूप से कुछ भी परिभाषित नहीं करता है। एक बीन विधि कॉल के पीछे सिर्फ चर है, [मेमोरी मॉडल] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4) जैसा लागू होता है। मेमोरी मॉडल परिप्रेक्ष्य से विधि कॉल नो-ऑप्स हैं। परमाणुता से मेरा मतलब है कि आपकी बीन असंगत स्थिति में समाप्त हो सकती है, यदि आपके पास ऐसे क्षेत्र हैं जो तर्कसंगत रूप से एक-दूसरे पर निर्भर करते हैं: उदाहरण के लिए, "प्रारंभ" समय "अंत" समय से पहले या कुछ ऐसा होना चाहिए - जब तक आपके पास गारंटी न हो उन दोनों क्षेत्रों को परमाणु रूप से बदलने के लिए एक तंत्र। –

+0

वैसे, 'अस्थिर' 'लंबा' और 'डबल' मानों के लिखते और पढ़ते हैं हमेशा परमाणु होते हैं। जेएलएस 17.7 देखें। –

4

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

आपके उदाहरण के लिए, बहु-थ्रेडेड कोड लिखने में कठिनाई यह है कि गलत कोड के कई रूप परीक्षण में ठीक काम करते हैं। सिर्फ इसलिए कि परीक्षण में बहु-थ्रेडेड परीक्षण "सफल" होता है इसका मतलब यह नहीं है कि यह सही कोड है।

विशिष्ट जेएलएस संदर्भ के लिए, Happens Before अनुभाग (और शेष पृष्ठ) देखें।

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

+0

यदि जेएसएल में कुछ भी नहीं कहता है कि क्षेत्र को अस्थिर घोषित किया जाना चाहिए, तो आप कैसे जानते हैं कि इसे घोषित किया जाना चाहिए अस्थिर के रूप में? Mutli- threaded कोड का परीक्षण करने के बारे में आपकी राय ठीक से अधिक है, लेकिन सवाल जेएसएल के बारे में था। – khachik

+0

@khachik - जेएलएस आपके कोड को थ्रेड-सुरक्षित बनाने के लिए आपको क्या करना चाहिए इसके विशिष्ट विवरण देता है। मैंने संदर्भित अनुभाग में कई प्रकार के विकल्प हैं। JLS _requires_ में कुछ भी नहीं, आप अपना कोड थ्रेड-सुरक्षित बनाने के लिए, इस प्रकार आपको फ़ील्ड को अस्थिर बनाना नहीं है (अन्यथा सभी फ़ील्ड डिफ़ॉल्ट रूप से अस्थिर हो जाएंगे)। – jtahlborn

+0

सुनिश्चित करें। यह मेरे प्रश्न का उत्तर नहीं देता है। – khachik

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