जावा में सिंक्रनाइज़ किए गए स्थिर तरीके कैसे काम करते हैं और क्या मैं इसे हाइबरनेट संस्थाओं को लोड करने के लिए उपयोग कर सकता हूं?


179

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

मैं एक ही DB उदाहरण के लिए जानकारी की पहुँच को रोकने के लिए यह चाहते हैं। हालाँकि, मुझे अब यकीन है कि अगर निम्नलिखित कोड getObjectByIdसभी वर्गों के लिए बुलाया जा रहा है जब इसे किसी विशेष वर्ग द्वारा बुलाया जाता है।

public class Utils {
     public static synchronized Object getObjectById (Class objclass, Long id) {
           // call hibernate class
         Session session = new Configuration().configure().buildSessionFactory().openSession();
         Object obj = session.load(objclass, id);
         session.close();
         return obj;
     }

     // other static methods
}

जवाबों:


136

एक स्थिर विधि लॉक पर सिंक्रनाइज़ का उपयोग करके आप वर्ग विधियों और विशेषताओं को सिंक्रनाइज़ करेंगे (उदाहरण के तरीकों और विशेषताओं के विपरीत)

तो आपकी धारणा सही है।

मैं सोच रहा था कि क्या विधि को सिंक्रनाइज़ करना थ्रेड-सुरक्षा सुनिश्चित करने के लिए सही तरीका है।

ज़रुरी नहीं। आपको अपने RDBMS को उस काम को करने देना चाहिए। वे इस तरह के सामान में अच्छे हैं।

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

इस परिदृश्य की कल्पना करें:

ग्राहक A और B, तालिका T के रिकॉर्ड X में विभिन्न जानकारी डालने का प्रयास करते हैं।

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

संभवतः हाइबरनेट प्रलेखन में "लेनदेन और संगामिति" अध्याय पर एक नज़र रखना बेहतर हो सकता है । जिन समस्याओं को आप हल करने का प्रयास कर रहे हैं, उनमें से अधिकांश पहले से ही हल हो गई हैं और बहुत बेहतर तरीके से।


1
बहुत उपयोगी जवाब! धन्यवाद! तो हाइबरनेट "आशावादी लॉकिंग" द्वारा cnocurrency की देखभाल करता है। फिर किसी भी डेटा-एक्सेस कॉन्क्रेसी को हल करने के लिए "सिंक्रनाइज़" विधियों का उपयोग करने की कोई आवश्यकता नहीं है ?? यदि डेटाबेस में डेटा संग्रहीत नहीं है, तो केवल "सिंक्रनाइज़" विधियों का उपयोग करें ?? ..जब आप उनका उपयोग करते हैं ??
टमाटर

1
1) मुझे लगता है कि निराशावादी लॉकिंग का उपयोग करने के लिए कुछ साधन भी हैं। 2) नहीं, RDBMS उस काम को कर सकता है। 3) यदि डेटा एक ही समय में कई थ्रेड द्वारा एक्सेस किया जाता है। 4) सिंक्रनाइज़ेशन उपयोगी है जब दो थ्रेड को डेटा साझा करना होता है। अगर वे करने की जरूरत नहीं है, तो बहुत बेहतर!
ऑस्कररेज़

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

5
"पूरी कक्षा" लॉक नहीं है। जावा मशीन भाषा विनिर्देश : For a class (static) method, the monitor associated with the Class object for the method's class is used. For an instance method, the monitor associated with this (the object for which the method was invoked) is used.इस प्रकार अगर एक धागा एक स्थिर विधि में प्रवेश करती है, एक ही वस्तु द्वारा दिया वस्तु # getClass बंद है। अन्य सूत्र अभी भी इंस्टेंस विधियों तक पहुँच सकते हैं।
मार्टिन एंडरसन

4
योग्य मुझे पता है कि मेरे स्वयं के शब्दांकन भी ऐसे नहीं हैं जो अंततः सही हों। मैंने कहा "इस प्रकार यदि एक धागा एक स्थिर विधि में प्रवेश करता है, तो ऑब्जेक्ट # getClass द्वारा लौटाया गया एक ही ऑब्जेक्ट लॉक होता है"। तकनीकी रूप से सही नहीं है। सभी जिज्ञासु लोगों के लिए लंबी कहानी छोटी है: आपके आवेदन में प्रत्येक वर्ग के लिए, एक Classवस्तु मौजूद है , जो वर्चुअल मशीन क्लास लोडर में से एक द्वारा तत्कालिक रूप से बनाई गई है। सभी वस्तुओं की तरह, यह ऑब्जेक्ट भी इसके Monitorसाथ जुड़ा हुआ है। और यह मॉनीटर जो बंद किया जा रहा है।
मार्टिन एंडरसन

233

प्रश्न को अधिक आम तौर पर संबोधित करने के लिए ...

ध्यान रखें कि विधियों पर सिंक्रनाइज़ का उपयोग करना वास्तव में सिर्फ आशुलिपि है (मान लीजिए कि क्लास SomeClass है):

synchronized static void foo() {
    ...
}

के समान है

static void foo() {
    synchronized(SomeClass.class) {
        ...
    }
}

तथा

synchronized void foo() {
    ...
}

के समान है

void foo() {
    synchronized(this) {
        ...
    }
}

आप लॉक के रूप में किसी भी ऑब्जेक्ट का उपयोग कर सकते हैं। यदि आप स्थैतिक विधियों के सबसेट को लॉक करना चाहते हैं, तो आप कर सकते हैं

class SomeClass {
    private static final Object LOCK_1 = new Object() {};
    private static final Object LOCK_2 = new Object() {};
    static void foo() {
        synchronized(LOCK_1) {...}
    }
    static void fee() {
        synchronized(LOCK_1) {...}
    }
    static void fie() {
        synchronized(LOCK_2) {...}
    }
    static void fo() {
        synchronized(LOCK_2) {...}
    }
}

(गैर-स्थैतिक तरीकों के लिए, आप ताले को गैर-स्थिर क्षेत्र बनाना चाहेंगे)


9
वे शीर्ष 4 कोड ब्लॉक सोने के हैं। ठीक वही जो मेरे द्वारा खोजा जा रहा था। धन्यवाद।
रयान शिलिंग्टन

क्या यह सही है कि अगर मैं गैर-स्थैतिक विधि पर एक स्थिर लॉक का उपयोग करता हूं, तो क्लासक्लास की कोई दो वस्तुएं एक ही समय में ब्लॉक को चलाने में सक्षम नहीं होंगी?
शमूएल

2
@ शमूएल - लगभग ... यह वस्तु उदाहरणों की तुलना में धागे के बारे में अधिक है। आप सही हैं कि SomeClass के अलग-अलग उदाहरण सभी एक ही लॉक / मॉनीटर का उपयोग करेंगे: एक जो Someclass.club ऑब्जेक्ट से जुड़ा है। इसलिए यदि दो अलग-अलग धागे SomeClass के दो अलग-अलग उदाहरणों को संसाधित कर रहे थे, तो वे दोनों एक ही समय में नहीं चल सकते थे। हालाँकि, अगर किसी थ्रेड को SomeClass के एक उदाहरण में एक विधि कहा जाता है, और उस विधि को दूसरी आवृत्ति में एक विधि कहा जाता है, तो कोई अवरोधन नहीं होगा।
स्कॉट स्टैनफ़ील्ड

@ScottStanchfield आपने विधियों को सिंक्रनाइज़ करने के तरीके सूचीबद्ध किए हैं, क्या वे सभी समान हैं?
Bionix1441

1
@ Bionix1441 - यह सब स्कूपिंग के बारे में है। ऊपर प्रत्येक तंत्र आपको लॉकिंग का बेहतर नियंत्रण देता है। सबसे पहले, इंस्टेंस का उपयोग करते हुए एक संपूर्ण विधि को लॉक करना, फिर किसी इंस्टेंस को किसी विधि के अंदर लॉक करना, फिर किसी ऑब्जेक्ट को सेक्शंस को लॉक करना।
स्कॉट स्टैनफ़ील्ड

17

स्टेटिक विधियाँ कक्षा को लॉकिंग के लिए ऑब्जेक्ट के रूप में उपयोग करती हैं, जो आपके उदाहरण के लिए Utils.class है। तो हाँ, यह ठीक है।


14

static synchronizedकक्षा के Classऑब्जेक्ट synchronizedपर लॉक रखने का मतलब है जहाँ उस क्लास की ऑब्जेक्ट पर लॉक रखने का मतलब है। इसका मतलब है, यदि आप एक थ्रेड (निष्पादन के) में एक गैर-स्थिर सिंक्रनाइज़ विधि तक पहुँच रहे हैं, तो आप अभी भी किसी अन्य थ्रेड का उपयोग करके एक स्थिर सिंक्रनाइज़ विधि तक पहुँच सकते हैं।

इसलिए, किसी थ्रेड से अधिक किसी भी समय दो समान तरीकों (या तो दो स्थिर या दो गैर-स्थिर तरीके) तक पहुंचना संभव नहीं है।


10

आप यह क्यों लागू करना चाहते हैं कि किसी एक समय में केवल एक धागा डीबी तक पहुंच सकता है?

यह डेटाबेस ड्राइवर का काम है कि वह किसी भी आवश्यक लॉकिंग को लागू कर सकेConnection में केवल एक धागे का उपयोग किया जाता है!

सबसे अधिक संभावना है, आपका डेटाबेस कई, समानांतर उपयोग से निपटने में पूरी तरह से सक्षम है


मैं यह शर्त लगा रहा हूं कि यह कुछ लेनदेन संबंधी समस्या के लिए एक समाधान था / है। यानी, सही समस्या हल नहीं होती है
मैट बी

1
मुझे नहीं पता था कि .... मुझे लगा कि मुझे इसे मैन्युअल रूप से लागू करना होगा। इस पर ध्यान दिलाने के लिए धन्यवाद! :)
टमाटर

2

यदि आपके डेटाबेस में डेटा के साथ कुछ करना है, तो डेटाबेस आइसोलेशन लॉकिंग का उपयोग क्यों न करें?


मेरे पास कोई डेटाबेस पृष्ठभूमि नहीं है। अब मुझे पता है!! इस पर ध्यान दिलाने के लिए धन्यवाद! :)
टमाटर

2

आपके प्रश्न का उत्तर देने के लिए, हाँ यह करता है: आपकी synchronizedविधि को एक समय में एक से अधिक थ्रेड द्वारा निष्पादित नहीं किया जा सकता है।


2

synchronizedजावा कीवर्ड कैसे काम करता है

जब आप जोड़ते हैं synchronized कीवर्ड को स्टैटिक विधि से , तो विधि को एक बार में केवल एक थ्रेड द्वारा बुलाया जा सकता है।

आपके मामले में, हर विधि कॉल करेगी:

  • कोई नया बनाएं SessionFactory
  • कोई नया बनाएं Session
  • इकाई लाना
  • कॉलर को इकाई वापस लौटाएं

हालाँकि, ये आपकी आवश्यकताएं थीं:

  • मैं चाहता हूँ कि यह एक ही DB उदाहरण के लिए जानकारी तक पहुँच को रोकने के लिए है।
  • रोकने getObjectByIdजब यह एक विशेष वर्ग से पुकारा जाता है सभी वर्गों के लिए बुलाया जा रहा है

इसलिए, भले ही getObjectByIdविधि थ्रेड-सुरक्षित हो, कार्यान्वयन गलत है।

SessionFactory सर्वोत्तम प्रथाएं

यह SessionFactoryथ्रेड-सुरक्षित है, और इसे बनाने के लिए एक बहुत महंगी वस्तु है क्योंकि इसमें इकाई वर्गों को पार्स करने और आंतरिक इकाई मेटामोडियम प्रतिनिधित्व का निर्माण करने की आवश्यकता है।

इसलिए, आपको SessionFactoryहर getObjectByIdविधि कॉल पर नहीं बनाना चाहिए ।

इसके बजाय, आपको इसके लिए एक एकल उदाहरण बनाना चाहिए।

private static final SessionFactory sessionFactory = new Configuration()
    .configure()
    .buildSessionFactory();

Sessionहमेशा बंद किया जाना चाहिए

आप बंद नहीं किया Sessionएक में finallyब्लॉक, और इस डाटाबेस संसाधन लीक कर सकते हैं अगर एक अपवाद जब इकाई लोड हो रहा है फेंक दिया है।

Session.loadJavaDoc एक HibernateExceptionइकाई में डेटाबेस में नहीं पाया जा सकता है, तो विधि के अनुसार ।

यदि उदाहरण मौजूद है ( get()इसके बजाय उपयोग ) यह निर्धारित करने के लिए आपको इस विधि का उपयोग नहीं करना चाहिए । इसका उपयोग केवल उस उदाहरण को प्राप्त करने के लिए करें जिसे आप मानते हैं कि मौजूद है, जहां गैर-अस्तित्व एक वास्तविक त्रुटि होगी।

इसलिए आपको इस तरह से इसे finallyबंद करने के लिए एक ब्लॉक का उपयोग करने की आवश्यकता है Session:

public static synchronized Object getObjectById (Class objclass, Long id) {    
     Session session = null;
     try {
         session = sessionFactory.openSession();
         return session.load(objclass, id);
     } finally {
         if(session != null) {
             session.close(); 
         }
     }
 }

बहु-धागा पहुंच को रोकना

आपके मामले में, आप यह सुनिश्चित करना चाहते थे कि केवल एक धागा उस विशेष इकाई तक पहुंच प्राप्त करे।

लेकिन synchronizedकीवर्ड केवल दो थ्रेड को कॉल करने से रोकता हैgetObjectById समवर्ती है। यदि दो धागे एक के बाद एक इस विधि को कहते हैं, तो आपके पास इस इकाई का उपयोग करने वाले दो धागे होंगे।

इसलिए, यदि आप किसी दिए गए डेटाबेस ऑब्जेक्ट को लॉक करना चाहते हैं तो कोई अन्य धागा इसे संशोधित नहीं कर सकता है, तो आपको डेटाबेस लॉक का उपयोग करने की आवश्यकता है।

synchronizedकीवर्ड केवल एक JVM में काम करता है। यदि आपके पास कई वेब नोड्स हैं, तो यह मल्टीपल जेवीएम के मल्टी-थ्रेड एक्सेस को नहीं रोक पाएगा।

DB को परिवर्तनों को लागू करते समय आपको क्या करने की आवश्यकता है, इसका उपयोग करें LockModeType.PESSIMISTIC_READयाLockModeType.PESSIMISTIC_WRITE इस तरह करें:

Session session = null;
EntityTransaction tx = null;

try {
    session = sessionFactory.openSession();

    tx = session.getTransaction();
    tx.begin();

    Post post = session.find(
        Post.class, 
        id, 
        LockModeType.LockModeType.PESSIMISTIC_READ
    );

    post.setTitle("High-Performance Java Perisstence");

    tx.commit();
} catch(Exception e) {
    LOGGER.error("Post entity could not be changed", e);
    if(tx != null) {
        tx.rollback(); 
    }
} finally {
    if(session != null) {
        session.close(); 
    }
}

तो यही वह है जो मैंने किया:

  • मैंने एक नया बनाया EntityTransactionऔर एक नया डेटाबेस लेनदेन शुरू किया
  • मैंने Postसंबद्ध डेटाबेस रिकॉर्ड पर ताला लगाते हुए इकाई को लोड किया
  • मैंने बदल दिया Post इकाई को और लेनदेन को प्रतिबद्ध किया
  • Exceptionफेंके जाने की स्थिति में , मैंने लेन-देन वापस कर दिया

एसीआईडी ​​और डेटाबेस लेनदेन के बारे में अधिक जानकारी के लिए, इस लेख को भी देखें।

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