क्या कारण है कि जावा 8 इंटरफ़ेस विधियों में "सिंक्रनाइज़" की अनुमति नहीं है?


210

जावा 8 में, मैं आसानी से लिख सकता हूं:

interface Interface1 {
    default void method1() {
        synchronized (this) {
            // Something
        }
    }

    static void method2() {
        synchronized (Interface1.class) {
            // Something
        }
    }
}

मुझे पूर्ण सिंक्रनाइज़ेशन शब्दार्थ मिलेगा जो मैं कक्षाओं में भी उपयोग कर सकता हूं। हालाँकि, मैं synchronizedपद्धति घोषणाओं में संशोधन का उपयोग नहीं कर सकता :

interface Interface2 {
    default synchronized void method1() {
        //  ^^^^^^^^^^^^ Modifier 'synchronized' not allowed here
    }

    static synchronized void method2() {
        // ^^^^^^^^^^^^ Modifier 'synchronized' not allowed here
    }
}

अब, कोई यह तर्क दे सकता है कि दो इंटरफेस एक ही तरह से व्यवहार करते हैं सिवाय इसके कि Interface2एक अनुबंध पर method1()और उस पर स्थापित method2()होता है, जो कि क्या Interface1करता है की तुलना में थोड़ा मजबूत है। बेशक, हम यह भी तर्क दे सकते हैं कि defaultकार्यान्वयन को ठोस कार्यान्वयन राज्य के बारे में कोई धारणा नहीं बनाना चाहिए, या ऐसा कोई कीवर्ड केवल अपना वजन नहीं बढ़ाएगा।

सवाल:

क्या कारण है कि JSR-335 विशेषज्ञ समूह ने synchronizedइंटरफ़ेस विधियों पर समर्थन नहीं करने का निर्णय लिया ?


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

@MartinStrejc: यह छोड़ने के लिए एक स्पष्टीकरण हो सकता है default synchronized, फिर भी इसके लिए जरूरी नहीं है static synchronized, हालांकि मैं स्वीकार करता हूं कि बाद वाले को निरंतरता कारणों से छोड़ दिया गया हो सकता है।
लुकास एडर

1
मुझे यकीन नहीं है कि अगर यह सवाल किसी भी मूल्य को जोड़ता है क्योंकि synchronizedउप-वर्ग में संशोधक को ओवरराइड किया जा सकता है, इसलिए यह केवल तभी महत्वपूर्ण होगा जब अंतिम डिफ़ॉल्ट विधियों के रूप में कुछ था। (आपका अन्य प्रश्न)
skiwi

@skiwi: ओवरराइडिंग तर्क पर्याप्त नहीं है। उपवर्गों को synchronizedसुपर क्लास में घोषित तरीकों को ओवरराइड कर सकते हैं , प्रभावी ढंग से सिंक्रनाइज़ेशन को हटा सकते हैं। मुझे आश्चर्य नहीं होगा कि समर्थन synchronizedऔर समर्थन नहीं finalकरना संबंधित है, हालांकि, शायद कई उत्तराधिकार (जैसे विरासत void x() और synchronized void x() , आदि) के कारण। लेकिन यह अटकलें हैं। मैं एक आधिकारिक कारण के बारे में उत्सुक हूं, अगर एक है।
लुकास एडर

2
>> "उपवर्ग उन विधियों को ओवरराइड कर सकते हैं जिन्हें सुपर क्लास में सिंक्रोनाइज़ किया जाता है, प्रभावी ढंग से सिंक्रोनाइजेशन को हटा दिया जाता है" ... केवल तभी नहीं जब वे कॉल न करें superजिसके लिए पूर्ण पुन: कार्यान्वयन और निजी सदस्यों तक संभव पहुंच की आवश्यकता होती है। Btw, एक कारण है कि उन तरीकों को "रक्षक" कहा जाता है - वे नए तरीकों को जोड़ने की आसान अनुमति देने के लिए मौजूद हैं।
bestsss

जवाबों:


260

हालांकि पहले यह स्पष्ट प्रतीत हो सकता है कि कोई synchronizedडिफ़ॉल्ट विधियों पर संशोधक का समर्थन करना चाहेगा , यह पता चलता है कि ऐसा करना खतरनाक होगा, और इसलिए निषिद्ध था।

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

तो, वे खतरनाक क्यों हैं? सिंक्रनाइज़ेशन लॉकिंग के बारे में है। लॉकिंग उत्परिवर्तनीय अवस्था में साझा पहुंच के समन्वय के बारे में है। प्रत्येक ऑब्जेक्ट में एक सिंक्रनाइज़ेशन नीति होनी चाहिए जो निर्धारित करती है कि कौन से राज्य के चर को लॉक करता है। ( अभ्यास में जावा कंसीडर देखें , खंड 2.4।)

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

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

किसी एकल स्रोत फ़ाइल के लिए लगातार सिंक्रनाइज़ेशन नीति बनाए रखना पहले से ही कठिन है; यह सुनिश्चित करना और भी कठिन है कि एक उपवर्ग सही ढंग से अपने सुपरक्लास द्वारा परिभाषित सिंक्रनाइज़ेशन नीति का पालन करता है। ऐसी शिथिल युग्मित कक्षाओं (एक इंटरफ़ेस और संभवतः इसे लागू करने वाले कई वर्ग) के बीच ऐसा करने की कोशिश करना लगभग असंभव और अत्यधिक त्रुटि-प्रवण होगा।

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


26
यह भी ध्यान दें कि JDK 1.1 में, synchronizedविधि संशोधक javadoc आउटपुट में प्रकट हुआ, लोगों को यह सोचकर गुमराह करता है कि यह विनिर्देश का हिस्सा था। यह JDK 1.2 में तय किया गया था। यहां तक ​​कि अगर यह एक सार्वजनिक पद्धति पर दिखाई देता है, तो synchronizedसंशोधक कार्यान्वयन का हिस्सा है, अनुबंध नहीं। ( nativeसंशोधक के लिए इसी तरह का तर्क और उपचार हुआ ।)
स्टुअर्ट मार्क्स

15
प्रारंभिक जावा कार्यक्रमों में एक सामान्य गलती पर्याप्त synchronizedऔर थ्रेड सुरक्षित घटकों को छिड़कना था और आपके पास लगभग थ्रेड सुरक्षित कार्यक्रम था। समस्या यह थी आमतौर पर ठीक काम किया, लेकिन यह आश्चर्यजनक और भंगुर तरीकों से टूट गया। मैं इस बात से सहमत हूं कि आपके लॉकिंग कार्य कैसे मजबूत अनुप्रयोगों के लिए महत्वपूर्ण हैं।
पीटर लॉरी

10
@BrianGoetz बहुत अच्छा कारण है। लेकिन synchronized(this) {...}एक defaultविधि में इसकी अनुमति क्यों है ? (जैसा कि लुकास के प्रश्न में दिखाया गया है।) क्या यह डिफ़ॉल्ट पद्धति को कार्यान्वयन वर्ग की स्थिति के मालिक होने की अनुमति नहीं देता है? क्या हम उसे भी नहीं रोकना चाहते? क्या हमें उन मामलों को खोजने के लिए फाइंडबग्स नियम की आवश्यकता होगी, जिनके लिए असंगत डेवलपर्स ऐसा करते हैं?
ज्योफ्री डी स्मेट

17
@Geoffrey: नहीं, इसे प्रतिबंधित करने का कोई कारण नहीं है (हालाँकि इसे हमेशा सावधानी के साथ इस्तेमाल किया जाना चाहिए।) सिंक ब्लॉक में लेखक को स्पष्ट रूप से लॉक ऑब्जेक्ट का चयन करने की आवश्यकता होती है; यह उन्हें किसी अन्य वस्तु के सिंक्रनाइज़ेशन पॉलिसी में भाग लेने की अनुमति देता है, अगर उन्हें पता है कि वह नीति क्या है। खतरनाक हिस्सा मान रहा है कि 'यह' (जो सिंक विधियां करते हैं) पर सिंक्रनाइज़ करना वास्तव में सार्थक है; यह एक अधिक स्पष्ट निर्णय होने की आवश्यकता है। उस ने कहा, मुझे उम्मीद है कि इंटरफ़ेस विधियों में सिंक ब्लॉक बहुत दुर्लभ हैं।
ब्रायन गोएट्ज

6
@GeoffreyDeSmet: उसी कारण से आप जैसे कर सकते हैं synchronized(vector)। यह आप सुरक्षित होना चाहते हैं, आपको thisलॉक करने के लिए कभी भी सार्वजनिक वस्तु (जैसे स्वयं) का उपयोग नहीं करना चाहिए ।
योगू

0
public class ParentSync {

public synchronized void parentStart() {
    System.out.println("I am " + this.getClass() + " . parentStarting. now:" + nowStr());
    try {
        Thread.sleep(30000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("I am " + this.getClass() + " . parentFinished. now" + nowStr());
}

private String nowStr() {
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
}
}


public class SonSync1 extends ParentSync {
public void sonStart() {
    System.out.println("I am " + this.getClass() + ". sonStarting,calling parent now ... ");
    super.parentStart();
    System.out.println("I am " + this.getClass() + ". sonFinished");
}
}



public class SonSync2 extends ParentSync {

public void sonStart() {
    System.out.println("I am " + this.getClass() + ". sonStarting,calling parent now ... ");
    super.parentStart();
    System.out.println("I am " + this.getClass() + ". sonFinished");
}
}



public class SyncTest {
public static void main(String[] args) throws Exception {

    new Thread(() -> {
        new SonSync1().sonStart();
    }).start();

    new Thread(() -> {
        new SonSync2().sonStart();
    }).start();

    System.in.read();
}
}

परिणाम:

I am class com.common.interface18_design.whynotsync_onmethod.SonSync1. sonStarting,calling parent now ... 
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2. sonStarting,calling parent now ... 
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2 . parentStarting. now:2019-04-18 09:50:08
I am class com.common.interface18_design.whynotsync_onmethod.SonSync1 . parentStarting. now:2019-04-18 09:50:08
I am class com.common.interface18_design.whynotsync_onmethod.SonSync1 . parentFinished. now2019-04-18 09:50:38
I am class com.common.interface18_design.whynotsync_onmethod.SonSync1. sonFinished
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2 . parentFinished. now2019-04-18 09:50:38
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2. sonFinished

(उदाहरण के लिए मूल वर्ग का उपयोग करने के लिए खेद है)

परिणाम से, हम यह जान सकते हैं कि मूल वर्ग लॉक प्रत्येक सब क्लास के स्वामित्व में है, SonSync1 और SonSync2 ऑब्जेक्ट में कोई ऑब्जेक्ट लॉक है। हर ताला स्वतंत्र है। इसलिए इस मामले में, मुझे लगता है कि माता-पिता वर्ग या सामान्य इंटरफ़ेस में सिंक्रनाइज़ का उपयोग करना खतरनाक नहीं है। किसी को भी इस पर और अधिक समझा सकता है?

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