BufferedInputStream सीधे फ़ील्ड का उपयोग करने के बजाय किसी फ़ील्ड को स्थानीय चर में कॉपी क्यों करता है


107

जब मैंने स्रोत कोड पढ़ा java.io.BufferedInputStream.getInIfOpen(), तो मैं इस बारे में उलझन में हूं कि इसने इस तरह कोड क्यों लिखा:

/**
 * Check to make sure that underlying input stream has not been
 * nulled out due to close; if not return it;
 */
private InputStream getInIfOpen() throws IOException {
    InputStream input = in;
    if (input == null)
        throw new IOException("Stream closed");
    return input;
}

यह inनीचे की तरह सीधे क्षेत्र चर का उपयोग करने के बजाय उपनाम का उपयोग क्यों कर रहा है :

/**
 * Check to make sure that underlying input stream has not been
 * nulled out due to close; if not return it;
 */
private InputStream getInIfOpen() throws IOException {
    if (in == null)
        throw new IOException("Stream closed");
    return in;
}

क्या कोई उचित स्पष्टीकरण दे सकता है?


में Eclipse, आप किसी ifविवरण पर डीबगर को रोक नहीं सकते । उस उर्फ ​​चर के लिए एक कारण हो सकता है। बस वहाँ से बाहर फेंकना चाहता था। मैं अटकलें लगाता हूं, बिल्कुल।
देबोसमित रे

@DebosmitRay: वास्तव में ifबयान पर विराम नहीं लगा सकते ?
rkosegi

@rkosegi ग्रहण के मेरे संस्करण पर, समस्या इस के समान है । बहुत सामान्य घटना नहीं हो सकती है। और वैसे भी, मेरा मतलब हल्के नोट पर नहीं था (स्पष्ट रूप से एक बुरा मजाक)। :)
देबोसमित रे

जवाबों:


119

यदि आप इस कोड को संदर्भ से बाहर देखते हैं तो उस "उर्फ" के लिए कोई अच्छी व्याख्या नहीं है। यह केवल अनावश्यक कोड या खराब कोड शैली है।

लेकिन संदर्भ यह है कि BufferedInputStreamएक ऐसा वर्ग है जिसे उपवर्गित किया जा सकता है, और यह कि इसे बहु-सूत्रीय संदर्भ में काम करने की आवश्यकता है।

जो क्लू inघोषित किया गया FilterInputStreamहै , वह है protected volatile। इसका मतलब है कि एक मौका है कि एक उपवर्ग में पहुंच सकता है और उसे सौंप सकता nullहै in। उस संभावना को देखते हुए, "उर्फ" वास्तव में एक दौड़ की स्थिति को रोकने के लिए है।

"उपनाम" के बिना कोड पर विचार करें

private InputStream getInIfOpen() throws IOException {
    if (in == null)
        throw new IOException("Stream closed");
    return in;
}
  1. थ्रेड ए कॉल्स getInIfOpen()
  2. थ्रेड ए मूल्यांकन करता है in == nullऔर देखता है कि inनहीं हैnull
  3. थ्रेड बी असाइन करता nullहैin
  4. थ्रेड ए निष्पादित करता है return in। जो लौटता है nullक्योंकि avolatile

"उपनाम" इसे रोकता है। अब inथ्रेड ए द्वारा सिर्फ एक बार पढ़ा जाता है। यदि थ्रेड ए के nullबाद थ्रेड बी असाइन inहोता है तो कोई बात नहीं। थ्रेड ए या तो एक अपवाद फेंक देगा या एक (गारंटी) गैर-शून्य मान लौटाएगा।


11
जो दिखाता है कि protectedबहु-सूत्रीय संदर्भ में चर क्यों बुरे हैं।
मिक मेमोनिक

2
यह वास्तव में करता है। हालाँकि, AFAIK ये कक्षाएं जावा 1.0 पर वापस जाती हैं। यह एक खराब डिजाइन निर्णय का एक और उदाहरण है जिसे ग्राहक कोड तोड़ने के डर से तय नहीं किया जा सकता है।
स्टीफन सी

2
@StephenC विस्तृत विवरण के लिए धन्यवाद +1। तो क्या इसका मतलब है, protectedअगर हमें यह बहु-थ्रेडेड है तो हमें अपने कोड में चर का उपयोग नहीं करना चाहिए ?
मधुसूदना रेड्डी सुननपु

3
@MadhusudanaReddySunnapu कुल मिलाकर सबक यह है कि एक ऐसे वातावरण में जहां कई सूत्र एक ही स्थिति तक पहुंच सकते हैं, आपको किसी तरह उस पहुंच को नियंत्रित करने की आवश्यकता है। यह एक निजी चर हो सकता है जो केवल सेटर के माध्यम से सुलभ हो, यह इस तरह से एक स्थानीय गार्ड हो सकता है, यह चर लेखन को-एक बार थ्रेडसेफ़ तरीके से बनाकर हो सकता है।
क्रिस हैस

3
@ sam - 1) यह सभी दौड़ की स्थिति और राज्यों को समझाने की आवश्यकता नहीं है। उत्तर का उद्देश्य इंगित करना है कि यह प्रतीत होता है कि अकथनीय कोड वास्तव में आवश्यक क्यों है। २) ऐसा कैसे?
स्टीफन C

20

इसकी वजह है क्लास BufferedInputStream को बहु-थ्रेडेड उपयोग के लिए डिज़ाइन किया गया है।

यहां, आप घोषणा को देखते हैं in, जिसे मूल वर्ग में रखा गया है FilterInputStream:

protected volatile InputStream in;

चूंकि यह है protected, इसके मूल्य को FilterInputStream, BufferedInputStreamऔर इसके उपवर्गों के किसी भी उपवर्ग द्वारा बदला जा सकता है । इसके अलावा, यह घोषित किया जाता है volatile, जिसका अर्थ है कि यदि कोई धागा चर के मूल्य को बदलता है, तो यह परिवर्तन तुरंत अन्य सभी थ्रेड्स में परिलक्षित होगा। यह संयोजन खराब है, क्योंकि इसका मतलब है कि वर्ग BufferedInputStreamको नियंत्रित करने या जानने का कोई तरीका नहीं है कि कब inबदला जाए। इस प्रकार, मान को अशक्त के लिए चेक और रिटर्न स्टेटमेंट के बीच भी बदला जा सकता है BufferedInputStream::getInIfOpen, जो प्रभावी रूप से चेक को बेकार बना देता है। inस्थानीय चर में कैश करने के लिए केवल एक बार के मूल्य को पढ़ने से input, विधि BufferedInputStream::getInIfOpenअन्य थ्रेड्स से परिवर्तन के खिलाफ सुरक्षित है, क्योंकि स्थानीय चर हमेशा एक ही धागे के स्वामित्व में होते हैं।

इसमें एक उदाहरण है BufferedInputStream::close, जो inअशक्त करता है:

public void close() throws IOException {
    byte[] buffer;
    while ( (buffer = buf) != null) {
        if (bufUpdater.compareAndSet(this, buffer, null)) {
            InputStream input = in;
            in = null;
            if (input != null)
                input.close();
            return;
        }
        // Else retry in case a new buf was CASed in fill()
    }
}

यदि निष्पादित होते BufferedInputStream::closeसमय किसी अन्य थ्रेड द्वारा कॉल किया जाता है BufferedInputStream::getInIfOpen, तो इसके परिणामस्वरूप ऊपर वर्णित दौड़ की स्थिति होगी।


मैं मानता हूँ, के रूप में यद्यपि हम जैसी चीजों को देख रहे हैं compareAndSet(), CASकोड में और टिप्पणियों में, आदि। मैंने BufferedInputStreamकोड भी खोजा और कई synchronizedविधियाँ पाईं। इसलिए, यह बहु-थ्रेडेड उपयोग के लिए अभिप्रेत है, हालांकि मुझे यकीन है कि इस तरह से इसका उपयोग कभी नहीं किया गया है। वैसे भी, मुझे लगता है कि आपका उत्तर सही है!
sparc_spread

यह शायद समझ में आता है क्योंकि getInIfOpen()केवल के public synchronizedतरीकों से कहा जाता है BufferedInputStream
मिक मेमोनिक

6

यह एक ऐसा छोटा कोड है, लेकिन, सैद्धांतिक रूप से, बहु-थ्रेडेड वातावरण में, inतुलना के बाद सही बदल सकता है, इसलिए विधि कुछ ऐसा लौटा सकती है जो यह जांच नहीं करता था (यह वापस आ सकता है null, इस प्रकार सटीक कार्य करने का मतलब था कैसे रोकें)।


क्या मैं सही हूं अगर मैं कहता हूं कि संदर्भ उस समय के बीच बदल in सकता है जब आप विधि और मूल्य की वापसी कहते हैं (एक बहु-थ्रेडेड वातावरण में)?
देबोसमित रे

हां, आप ऐसा कह सकते हैं। अंतत: संभावना वास्तव में ठोस मामले पर निर्भर करेगी (हम एक तथ्य के लिए जानते हैं कि inकभी भी बदल सकते हैं)।
acdcjunior

4

मेरा मानना inहै कि स्थानीय चर को वर्ग चर पर कब्जा करना inputअसंगत व्यवहार को रोकने के लिए है यदि चल रहा है, तो inएक और धागा द्वारा बदल दिया जाता getInIfOpen()है।

ध्यान दें कि मालिक का inमूल वर्ग है और वह इसे चिह्नित नहीं करता है final

इस पैटर्न को कक्षा के अन्य हिस्सों में दोहराया जाता है और यह उचित रक्षात्मक कोडिंग लगता है।

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