2012-01-11 15 views
8

जेएमएम से संबंधित प्रश्न और सिंक्रनाइज़ किए गए ब्लॉक में लिखे गए अस्थिर फ़ील्ड से संबंधित अर्थशास्त्र, लेकिन अन-सिंक्रनाइज़ेशन पढ़ें।अस्थिर सरणी की आवश्यकता सिंक्रनाइज़ ब्लॉक

नीचे दिए गए कोड के प्रारंभिक संस्करण में, मैं एक्सेस को सिंक्रनाइज़ नहीं कर रहा था क्योंकि यह पहले की आवश्यकताओं के लिए अनावश्यक था (और एक स्वयं असाइनमेंट का दुरुपयोग इस.cache = this.cache अस्थिर लेखन अर्थशास्त्र सुनिश्चित करता है)। कुछ आवश्यकताएं बदल गई हैं, यह सुनिश्चित करने के लिए सिंक्रनाइज़ेशन की आवश्यकता है कि डुप्लिकेट अपडेट नहीं भेजे गए हैं। मेरे पास सवाल यह है कि सिंक्रनाइज़ेशन ब्लॉक अस्थिर क्षेत्र के स्वयं असाइनमेंट की आवश्यकता को रोकता है?

// Cache of byte[] data by row and column. 
    private volatile byte[][][] cache; 

    public byte[] getData(int row, int col) 
    { 
    return cache[row][col]; 
    } 

    public void updateData(int row, int col, byte[] data) 
    { 
    synchronized(cache) 
    { 
     if (!Arrays.equals(data,cache[row][col])) 
     { 
     cache[row][col] = data; 

     // Volatile write. 
     // The below line is intentional to ensure a volatile write is 
     // made to the array, since access via getData is unsynchronized. 
     this.cache = this.cache; 

     // Notification code removed 
     // (mentioning it since it is the reason for synchronizing). 
     } 
    } 
    } 

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

उत्तर

4

हां, आपको अभी भी अस्थिर लेखन की आवश्यकता है, जावा मेमोरी मॉडल के अनुसार, आपको अभी भी कोई गारंटी नहीं है कि धागे सबसे अद्यतित मूल्य को पढ़ेंगे। वहाँ cache अनलॉक cache के बाद में एक अस्थिर पढ़ने के लिए की ओर से कोई तुल्यकालन आदेश है: अनलॉक -> volatileRead दृश्यता की गारंटी नहीं है। आपको या तो अनलॉक की आवश्यकता है -> लॉक या volatileWrite -> volatileRead

हालांकि, वास्तविक जेवीएम में बहुत मजबूत स्मृति गारंटी है। आमतौर पर अनलॉक और volatileWrite समान स्मृति प्रभाव है (भले ही वे विभिन्न चर पर हैं); लॉक और volatileRead के समान।

तो हमें यहां एक दुविधा है।सामान्य सिफारिश यह है कि आपको कड़ाई से spec का पालन करना चाहिए। जब तक आप इस मामले का बहुत व्यापक ज्ञान नहीं लेते। उदाहरण के लिए, एक जेडीके कोड कुछ चालें नियोजित कर सकता है जो सैद्धांतिक रूप से सही नहीं हैं; लेकिन कोड एक विशिष्ट जेवीएम को लक्षित कर रहा है और लेखक एक विशेषज्ञ है।

अतिरिक्त अस्थिर लेखन का सापेक्ष ओवरहेड वैसे भी बड़ा प्रतीत नहीं होता है।

आपका कोड सही और कुशल है; हालांकि यह ठेठ पैटर्न के बाहर है; मैं इसे एक छोटा सा tweak की तरह:

private final byte[][][] cacheF = new ...; // dimensions fixed? 
    private volatile byte[][][] cacheV = cacheF; 

    public byte[] getData(int row, int col) 
    { 
    return cacheV[row][col]; 
    } 

    public void updateData(int row, int col, byte[] data) 
    { 
    synchronized(cacheF) 
    { 
     if (!Arrays.equals(data,cacheF[row][col])) 
     { 
     cacheF[row][col] = data; 

     cacheV = cacheF; 
     } 
    } 
    } 
+0

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

3

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

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

+0

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

+0

+1, लेकिन मैंने दूसरे उत्तर को स्वीकार कर लिया क्योंकि यह अधिक सीधे प्रश्न का उत्तर देता है। –

3

एक सूचकांक एक अस्थिर सरणी को लिखता है वास्तव में कोई स्मृति प्रभाव नहीं होता है। यही है, यदि आप पहले ही सरणी को तुरंत चालू कर चुके हैं, तो फ़ील्ड को अस्थिर घोषित करने के लिए आपको सरणी के भीतर तत्वों को निर्दिष्ट करते समय स्मृति की शब्दावली नहीं मिलती है।

दूसरे शब्दों

private volatile byte[][]cache = ...; 
cache[row][col] = data; 

में के रूप में

private final byte[][]cache = ...; 
cache[row][col] = data; 
यह आप सभी पढ़ता है और सरणी के लिए लिखते हैं पर सिंक्रनाइज़ चाहिए वजह से

उसी स्मृति अर्थ विज्ञान है। और जब मैं 'वही स्मृति अर्थशास्त्र' कहता हूं, मेरा मतलब है कि थ्रेड्स cache[row][col]

+0

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

+1

हालांकि समस्या यह नहीं है कि गैर-वाष्पशील स्टोर और इस.cache = this.cache –

+1

की अस्थिर-दुकान के बीच संबंध से पहले कोई ऐसा नहीं होता है, क्योंकि एक गैर-अस्थिर लेखन कैश [पंक्ति] [col] एक ही थ्रेड में, कैश को अस्थिर लिखने से पहले बनाया जाता है। और अस्थिर लेखन कैश के पढ़ने से पहले होता है। –

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