क्या कोई मुझे उदाहरण के साथ सिंक्रोनाइज़्ड ब्लॉक पर सिंक्रोनाइज़ की गई विधि का फायदा बता सकता है?
क्या कोई मुझे उदाहरण के साथ सिंक्रोनाइज़्ड ब्लॉक पर सिंक्रोनाइज़ की गई विधि का फायदा बता सकता है?
जवाबों:
क्या कोई मुझे एक उदाहरण के साथ सिंक्रनाइज़ किए गए ब्लॉक पर सिंक्रनाइज़ की गई विधि का लाभ बता सकता है? धन्यवाद।
ब्लॉक पर सिंक्रनाइज़ विधि का उपयोग करने का कोई स्पष्ट लाभ नहीं है।
शायद केवल एक (लेकिन मैं इसे एक लाभ नहीं कहूंगा) क्या आपको ऑब्जेक्ट संदर्भ को शामिल करने की आवश्यकता नहीं है this।
तरीका:
public synchronized void method() { // blocks "this" from here....
...
...
...
} // to here
खंड मैथा:
public void method() {
synchronized( this ) { // blocks "this" from here ....
....
....
....
} // to here...
}
देख? कोई फायदा नहीं हुआ।
ब्लाकों करते ज्यादातर लचीलापन में हालांकि विधियों का लाभ है, क्योंकि आप लॉक के रूप में किसी अन्य वस्तु का उपयोग कर सकते विधि को सिंक करना संपूर्ण वस्तु ताला होगा जबकि।
की तुलना करें:
// locks the whole object
...
private synchronized void someInputRelatedWork() {
...
}
private synchronized void someOutputRelatedWork() {
...
}
बनाम
// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();
private void someInputRelatedWork() {
synchronized(inputLock) {
...
}
}
private void someOutputRelatedWork() {
synchronized(outputLock) {
...
}
}
इसके अलावा अगर विधि बढ़ती है तो आप अभी भी सिंक्रनाइज़ किए गए खंड को अलग रख सकते हैं:
private void method() {
... code here
... code here
... code here
synchronized( lock ) {
... very few lines of code here
}
... code here
... code here
... code here
... code here
}
synchronizedब्लॉक दो निर्देशों का उपयोग कर कार्यान्वित किया जाता है, monitorenterऔर monitorexit, प्लस अपवाद संचालक जो यह सुनिश्चित करता है monitorexitयहां तक कि असाधारण मामले में कहा जाता है। एक synchronizedविधि का उपयोग करते समय सभी को बचाया जाता है।
एकमात्र वास्तविक अंतर यह है कि एक सिंक्रनाइज़ किया गया ब्लॉक चुन सकता है कि वह किस ऑब्जेक्ट पर सिंक्रोनाइज़ करता है। एक सिंक्रनाइज़ विधि केवल 'this'(या एक सिंक्रनाइज़ क्लास विधि के लिए संबंधित वर्ग उदाहरण) का उपयोग कर सकती है। उदाहरण के लिए, ये शब्दार्थ समतुल्य हैं:
synchronized void foo() {
...
}
void foo() {
synchronized (this) {
...
}
}
उत्तरार्द्ध अधिक लचीला है क्योंकि यह किसी भी वस्तु के संबद्ध लॉक के लिए प्रतिस्पर्धा कर सकता है , अक्सर एक सदस्य चर। यह अधिक बारीक भी है क्योंकि आपके पास ब्लॉक के पहले और बाद में समवर्ती कोड निष्पादित हो सकता है लेकिन फिर भी विधि के भीतर। बेशक, आप समवर्ती विधि को आसानी से अलग-अलग गैर-सिंक्रनाइज़ किए गए तरीकों में समवर्ती कोड को रीक्रिएट करके उपयोग कर सकते हैं। जो भी कोड का उपयोग करता है वह अधिक समझ में आता है।
पेशेवरों:
विपक्ष:
पेशेवरों:
विपक्ष:
व्यक्तिगत रूप से मैं सिंक्रनाइज़ किए गए तरीकों का उपयोग करना पसंद करता हूं, जो केवल सिंक्रनाइज़ेशन की आवश्यकता वाली चीजों पर केंद्रित है। ऐसा वर्ग जितना संभव हो उतना छोटा होना चाहिए और इसलिए सिंक्रनाइज़ेशन की समीक्षा करना आसान होना चाहिए। दूसरों को सिंक्रनाइज़ेशन के बारे में ध्यान रखने की आवश्यकता नहीं होनी चाहिए।
मुख्य अंतर यह है कि यदि आप एक सिंक्रनाइज़ किए गए ब्लॉक का उपयोग करते हैं तो आप इसके अलावा किसी अन्य वस्तु पर लॉक कर सकते हैं जो अधिक लचीला होने की अनुमति देता है।
मान लें कि आपके पास एक संदेश कतार और कई संदेश निर्माता और उपभोक्ता हैं। हम उत्पादकों को एक-दूसरे के साथ हस्तक्षेप नहीं करना चाहते हैं, लेकिन उपभोक्ताओं को उत्पादकों के लिए इंतजार किए बिना संदेश प्राप्त करने में सक्षम होना चाहिए। तो हम सिर्फ एक वस्तु बनाते हैं
Object writeLock = new Object();
और अब से हर समय एक निर्माता एक नया संदेश जोड़ना चाहता है जिसे हम केवल उस पर लॉक करते हैं:
synchronized(writeLock){
// do something
}
इसलिए उपभोक्ता अभी भी पढ़ सकते हैं, और उत्पादकों को बंद कर दिया जाएगा।
सिंक्रोनाइज्ड विधि
सिंक्रोनाइज़्ड तरीकों के दो प्रभाव होते हैं।
सबसे पहले, जब एक धागा किसी ऑब्जेक्ट के लिए एक सिंक्रनाइज़ किए गए तरीके को निष्पादित कर रहा है, तो अन्य सभी थ्रेड्स जो ऑब्जेक्ट के साथ पहला थ्रेड तब तक ब्लॉक किए जाते हैं, जब तक कि एक ही ऑब्जेक्ट ब्लॉक (सस्पेंडेड एक्जीक्यूशन) के लिए सिंक्रनाइज़ किए गए तरीकों को लागू न करें।
दूसरा, जब एक सिंक्रनाइज़ की गई विधि बाहर निकलती है, तो यह स्वचालित रूप से एक ही ऑब्जेक्ट के लिए एक सिंक्रनाइज़ किए गए विधि के किसी भी बाद के आह्वान के साथ एक-पहले संबंध स्थापित करता है। यह गारंटी देता है कि ऑब्जेक्ट की स्थिति में परिवर्तन सभी थ्रेड्स को दिखाई देते हैं।
ध्यान दें कि कंस्ट्रक्टर को सिंक्रनाइज़ नहीं किया जा सकता है - एक कंस्ट्रक्टर के साथ सिंक्रनाइज़ कीवर्ड का उपयोग करना एक सिंटैक्स त्रुटि है। कंस्ट्रक्टर्स को सिंक्रोनाइज़ करने का कोई मतलब नहीं है, क्योंकि जिस थ्रेड से कोई ऑब्जेक्ट बनता है, उसका निर्माण करते समय उसकी पहुंच होनी चाहिए।
सिंक्रोनाइज़्ड स्टेटमेंट
सिंक्रनाइज़ किए गए तरीकों के विपरीत, सिंक्रनाइज़ किए गए कथनों को उस ऑब्जेक्ट को निर्दिष्ट करना होगा जो आंतरिक लॉक प्रदान करता है: अक्सर मैं इसका उपयोग किसी सूची या मानचित्र पर एक्सेस को सिंक्रनाइज़ करने के लिए करता हूं, लेकिन मैं ऑब्जेक्ट के सभी तरीकों तक पहुंच को अवरुद्ध नहीं करना चाहता।
प्रश्न: आंतरिक लॉक और सिंक्रोनाइज़ेशन सिंक्रोनाइज़ेशन आंतरिक इकाई के आसपास बनाया गया है जिसे आंतरिक लॉक या मॉनिटर लॉक के रूप में जाना जाता है। (एपीआई विनिर्देश अक्सर इस इकाई को केवल "मॉनिटर" के रूप में संदर्भित करता है) आंतरिक ताले सिंक्रनाइज़ेशन के दोनों पहलुओं में एक भूमिका निभाते हैं: किसी वस्तु की स्थिति के लिए अनन्य पहुंच को लागू करना और ऐसा होना चाहिए-पहले रिश्ते जो दृश्यता के लिए आवश्यक हैं।
प्रत्येक वस्तु में एक आंतरिक ताला जुड़ा होता है। कन्वेंशन द्वारा, एक थ्रेड जिसे किसी ऑब्जेक्ट के फ़ील्ड में अनन्य और लगातार एक्सेस की आवश्यकता होती है, उन्हें एक्सेस करने से पहले ऑब्जेक्ट के आंतरिक लॉक का अधिग्रहण करना पड़ता है, और फिर आंतरिक लॉक को तब जारी करते हैं जब यह उनके साथ हो जाता है। यह कहा जाता है कि लॉक को प्राप्त करने और लॉक जारी करने के बीच के समय में एक धागे को आंतरिक लॉक कहा जाता है। जब तक एक धागा एक आंतरिक लॉक का मालिक होता है, कोई भी अन्य धागा एक ही लॉक का अधिग्रहण नहीं कर सकता है। जब वह लॉक हासिल करने का प्रयास करेगा तो दूसरा धागा ब्लॉक हो जाएगा।
package test;
public class SynchTest implements Runnable {
private int c = 0;
public static void main(String[] args) {
new SynchTest().test();
}
public void test() {
// Create the object with the run() method
Runnable runnable = new SynchTest();
Runnable runnable2 = new SynchTest();
// Create the thread supplying it with the runnable object
Thread thread = new Thread(runnable,"thread-1");
Thread thread2 = new Thread(runnable,"thread-2");
// Here the key point is passing same object, if you pass runnable2 for thread2,
// then its not applicable for synchronization test and that wont give expected
// output Synchronization method means "it is not possible for two invocations
// of synchronized methods on the same object to interleave"
// Start the thread
thread.start();
thread2.start();
}
public synchronized void increment() {
System.out.println("Begin thread " + Thread.currentThread().getName());
System.out.println(this.hashCode() + "Value of C = " + c);
// If we uncomment this for synchronized block, then the result would be different
// synchronized(this) {
for (int i = 0; i < 9999999; i++) {
c += i;
}
// }
System.out.println("End thread " + Thread.currentThread().getName());
}
// public synchronized void decrement() {
// System.out.println("Decrement " + Thread.currentThread().getName());
// }
public int value() {
return c;
}
@Override
public void run() {
this.increment();
}
}
क्रॉस जांच अलग-अलग आउटपुट को सिंक्रोनाइज़ करने की विधि, ब्लॉक और बिना सिंक्रोनाइज़ेशन के।
जब जावा संकलक आपके स्रोत कोड को बाइट कोड में परिवर्तित करता है, तो यह सिंक्रनाइज़ किए गए तरीकों और सिंक्रनाइज़ किए गए ब्लॉक को बहुत अलग तरीके से संभालता है।
जब JVM एक सिंक्रनाइज़ विधि को निष्पादित करता है, तो निष्पादित थ्रेड पहचानता है कि विधि के method_info संरचना में ACC_SYNCHRONIZED ध्वज सेट है, तो यह स्वचालित रूप से ऑब्जेक्ट का लॉक प्राप्त करता है, विधि को कॉल करता है, और लॉक को रिलीज़ करता है। यदि कोई अपवाद होता है, तो थ्रेड स्वचालित रूप से लॉक रिलीज़ करता है।
दूसरी ओर, एक विधि ब्लॉक को सिंक्रनाइज़ करना, किसी ऑब्जेक्ट के लॉक और अपवाद हैंडलिंग को प्राप्त करने के लिए जेवीएम के अंतर्निहित समर्थन को बायपास करता है और आवश्यकता है कि कार्यक्षमता को बाइट कोड में स्पष्ट रूप से लिखा जाए। यदि आप एक सिंक्रनाइज़ ब्लॉक के साथ विधि के लिए बाइट कोड पढ़ते हैं, तो आपको इस कार्यक्षमता को प्रबंधित करने के लिए एक दर्जन से अधिक अतिरिक्त संचालन दिखाई देंगे।
यह एक सिंक्रनाइज़ विधि और एक सिंक्रनाइज़ ब्लॉक दोनों को उत्पन्न करने के लिए कॉल दिखाता है:
public class SynchronizationExample {
private int i;
public synchronized int synchronizedMethodGet() {
return i;
}
public int synchronizedBlockGet() {
synchronized( this ) {
return i;
}
}
}
synchronizedMethodGet()विधि निम्न बाइट कोड उत्पन्न करता है:
0: aload_0
1: getfield
2: nop
3: iconst_m1
4: ireturn
और यहाँ synchronizedBlockGet()विधि से बाइट कोड है :
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: getfield
6: nop
7: iconst_m1
8: aload_1
9: monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
सिंक्रनाइज़ विधि और ब्लॉक के बीच एक महत्वपूर्ण अंतर यह है कि, सिंक्रोनाइज़्ड ब्लॉक आमतौर पर लॉक के दायरे को कम करता है। जैसा कि लॉक का दायरा प्रदर्शन के व्युत्क्रमानुपाती होता है, कोड के केवल महत्वपूर्ण खंड को लॉक करने के लिए इसका हमेशा बेहतर होता है। सिंक्रोनाइज़्ड ब्लॉक का उपयोग करने का एक सबसे अच्छा उदाहरण सिंग्लटन पैटर्न में डबल चेक किया गया लॉकिंग है जहां पूरी getInstance()विधि को लॉक करने के बजाय हम केवल कोड के महत्वपूर्ण सेक्शन को लॉक करते हैं जो कि सिंगलटन इंस्टेंस बनाने के लिए उपयोग किया जाता है। यह प्रदर्शन में काफी सुधार करता है क्योंकि लॉकिंग केवल एक या दो बार आवश्यक है।
सिंक्रोनाइज़्ड मेथड्स का उपयोग करते समय, आपको स्टैटिक सिंक्रोनाइज़ और नॉन-स्टैटिक सिंक्रोनाइज़्ड मेथड्स दोनों को मिलाने पर अतिरिक्त ध्यान रखने की ज़रूरत होगी।
monitorenterऔर monitorexitकोड चलाने से पहले जोड़ देगा ।
अक्सर मैं इसका उपयोग किसी सूची या मानचित्र तक पहुंच को सिंक्रनाइज़ करने के लिए करता हूं, लेकिन मैं ऑब्जेक्ट के सभी तरीकों तक पहुंच को अवरुद्ध नहीं करना चाहता।
निम्न कोड में सूची को संशोधित करने वाला एक धागा मानचित्र को संशोधित करने वाले थ्रेड के लिए प्रतीक्षा को अवरुद्ध नहीं करेगा। यदि विधियों को ऑब्जेक्ट पर सिंक्रनाइज़ किया गया था, तो प्रत्येक विधि को इंतजार करना होगा, हालांकि वे जो संशोधन कर रहे हैं वे संघर्ष नहीं करेंगे।
private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();
public void put( String s, Bar b ) {
synchronized( myMap ) {
myMap.put( s,b );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public void hasKey( String s, ) {
synchronized( myMap ) {
myMap.hasKey( s );
}
}
public void add( Foo f ) {
synchronized( myList ) {
myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public Thing getMedianFoo() {
Foo med = null;
synchronized( myList ) {
Collections.sort(myList);
med = myList.get(myList.size()/2);
}
return med;
}
सिंक्रनाइज़ किए गए ब्लॉक के साथ, आपके पास कई सिंक्रोनाइज़र हो सकते हैं, ताकि एक साथ कई लेकिन गैर-परस्पर विरोधी चीजें एक ही समय में चल सकें।
परावर्तन विधियों का उपयोग प्रतिबिंब एपीआई का उपयोग करके किया जा सकता है। यह कुछ अनुबंधों के परीक्षण के लिए उपयोगी हो सकता है, जैसे कि मॉडल के सभी तरीके सिंक्रनाइज़ हैं ।
निम्नलिखित स्निपेट हैशटेबल के सभी सिंक्रनाइज़ किए गए तरीकों को प्रिंट करता है:
for (Method m : Hashtable.class.getMethods()) {
if (Modifier.isSynchronized(m.getModifiers())) {
System.out.println(m);
}
}
सिंक्रनाइज़ किए गए ब्लॉक का उपयोग करने पर महत्वपूर्ण ध्यान दें: सावधान रहें कि आप लॉक ऑब्जेक्ट के रूप में क्या उपयोग करते हैं!
ऊपर उपयोगकर्ता 2277816 से कोड स्निपेट इस बिंदु को दिखाता है कि एक स्ट्रिंग शाब्दिक का संदर्भ लॉकिंग ऑब्जेक्ट के रूप में उपयोग किया जाता है। एहसास करें कि स्ट्रिंग शाब्दिक जावा में स्वचालित रूप से इंटर्न होते हैं और आपको समस्या को देखना शुरू करना चाहिए: कोड का प्रत्येक टुकड़ा जो शाब्दिक "लॉक" पर सिंक्रनाइज़ होता है, उसी लॉक को साझा करता है! यह आसानी से कोड के पूरी तरह से असंबंधित टुकड़ों के साथ गतिरोध पैदा कर सकता है।
यह केवल स्ट्रिंग ऑब्जेक्ट्स नहीं है जिनसे आपको सावधान रहने की आवश्यकता है। बॉक्सिंग प्राइमिटिव्स भी एक खतरा है, क्योंकि ऑटोबॉक्सिंग और वैल्यूऑफ तरीके समान ऑब्जेक्ट्स का पुन: उपयोग कर सकते हैं, जो मूल्य पर निर्भर करता है।
अधिक जानकारी के लिए देखें: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may/be+reused
अक्सर विधि स्तर पर लॉक का उपयोग करना बहुत अशिष्ट होता है। क्यों कोड का एक टुकड़ा है कि किसी भी साझा संसाधनों का उपयोग नहीं करता है एक पूरी विधि बंद करके। चूंकि प्रत्येक ऑब्जेक्ट में एक लॉक होता है, आप ब्लॉक लेवल सिंक्रोनाइज़ेशन को लागू करने के लिए डमी ऑब्जेक्ट बना सकते हैं। ब्लॉक स्तर अधिक कुशल है क्योंकि यह पूरी विधि को लॉक नहीं करता है।
यहाँ कुछ उदाहरण हैं
विधि का स्तर
class MethodLevel {
//shared among threads
SharedResource x, y ;
public void synchronized method1() {
//multiple threads can't access
}
public void synchronized method2() {
//multiple threads can't access
}
public void method3() {
//not synchronized
//multiple threads can access
}
}
ब्लॉक स्तर
class BlockLevel {
//shared among threads
SharedResource x, y ;
//dummy objects for locking
Object xLock = new Object();
Object yLock = new Object();
public void method1() {
synchronized(xLock){
//access x here. thread safe
}
//do something here but don't use SharedResource x, y
// because will not be thread-safe
synchronized(xLock) {
synchronized(yLock) {
//access x,y here. thread safe
}
}
//do something here but don't use SharedResource x, y
//because will not be thread-safe
}//end of method1
}
[संपादित करें]
के लिए Collectionकी तरह Vectorहै और Hashtableवे सिंक्रनाइज़ जब कर रहे हैं ArrayListया HashMapनहीं कर रहे हैं और आप कीवर्ड सिंक्रनाइज़ या आह्वान संग्रह सिंक्रनाइज़ विधि निर्धारित करने की आवश्यकता:
Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
एकमात्र अंतर: सिंक्रोनाइज़्ड ब्लॉक दानेदार लॉकिंग को सिंक्रोनाइज़ करने की विधि के विपरीत अनुमति देता है
मूल रूप से synchronizedब्लॉक या विधियों का उपयोग मेमोरी असंगति त्रुटियों से बचने के लिए थ्रेड सेफ कोड लिखने के लिए किया गया है।
यह सवाल बहुत पुराना है और पिछले 7 वर्षों के दौरान कई चीजों को बदल दिया गया है। थ्रेड सुरक्षा के लिए नए प्रोग्रामिंग कंस्ट्रक्शन पेश किए गए हैं।
आप synchroniedब्लॉक के बजाए उन्नत संगामिति API का उपयोग करके थ्रेड सुरक्षा प्राप्त कर सकते हैं । यह प्रलेखन पृष्ठ थ्रेड सुरक्षा प्राप्त करने के लिए अच्छे प्रोग्रामिंग कंस्ट्रक्शन प्रदान करता है।
लॉक ऑब्जेक्ट्स लॉकिंग मुहावरों का समर्थन करते हैं जो कई समवर्ती अनुप्रयोगों को सरल बनाते हैं।
निष्पादकों शुरू करने और धागे के प्रबंधन के लिए एक उच्च स्तरीय एपीआई परिभाषित करते हैं। Java.util.concurrent द्वारा प्रदान किए गए निष्पादन कार्यान्वयन बड़े पैमाने के अनुप्रयोगों के लिए उपयुक्त थ्रेड पूल प्रबंधन प्रदान करते हैं।
समवर्ती संग्रह डेटा के बड़े संग्रह को प्रबंधित करना आसान बनाते हैं, और सिंक्रनाइज़ेशन की आवश्यकता को बहुत कम कर सकते हैं।
परमाणु चर में ऐसी विशेषताएं होती हैं जो सिंक्रनाइज़ेशन को कम करती हैं और मेमोरी संगतता त्रुटियों से बचने में मदद करती हैं।
थ्रेडलोकल्रैंडम (JDK 7 में) कई थ्रेड्स से कुशल जेनरेशन की छद्म संख्या प्रदान करता है।
सिंक्रोनाइज़ के लिए बेहतर रिप्लेसमेंट ReentrantLock है , जो LockAPI का उपयोग करता है
एक मूल पारस्परिक बहिष्कार लॉक एक ही मूल व्यवहार और शब्दार्थ के साथ है, क्योंकि अंतर्निहित मॉनिटर लॉक का उपयोग सिंक्रनाइज़ तरीकों और बयानों के साथ किया जाता है, लेकिन विस्तारित क्षमताओं के साथ।
ताले के साथ उदाहरण:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
अन्य प्रोग्रामिंग कंस्ट्रक्शन के लिए java.util.concurrent और java.util.concurrent.atomic पैकेज देखें ।
इस संबंधित प्रश्न का संदर्भ लें:
सामान्य तौर पर ये ऑब्जेक्ट के मॉनिटर के बारे में स्पष्ट होने के अलावा ज्यादातर वही होते हैं जिनका उपयोग इस ऑब्जेक्ट के बनाम इस ऑब्जेक्ट का उपयोग किया जा रहा है। सिंक्रनाइज़ किए गए तरीकों में से एक जो मुझे लगता है कि कभी-कभी अनदेखा होता है, यह है कि आप पर सिंक्रनाइज़ करने के लिए "यह" संदर्भ का उपयोग करने से बाहरी वस्तुओं को एक ही वस्तु पर लॉक करने की संभावना को खुला छोड़ दिया जाता है। यदि आप इसमें भाग लेते हैं तो यह बहुत ही सूक्ष्म बग हो सकता है। आंतरिक स्पष्ट ऑब्जेक्ट या अन्य मौजूदा फ़ील्ड पर सिंक्रोनाइज़ करना इस समस्या से पूरी तरह से सिंक्रनाइज़ेशन को बाधित कर सकता है।
जैसा कि पहले ही कहा गया है कि सिंक्रनाइज़ किए गए ब्लॉक उपयोगकर्ता द्वारा परिभाषित चर का उपयोग लॉक ऑब्जेक्ट के रूप में कर सकते हैं, जब सिंक्रनाइज़ फ़ंक्शन केवल "यह" का उपयोग करता है। और निश्चित रूप से आप अपने फ़ंक्शन के क्षेत्रों के साथ हेरफेर कर सकते हैं जिसे सिंक्रनाइज़ किया जाना चाहिए। लेकिन हर कोई कहता है कि सिंक्रनाइज़ फ़ंक्शन और ब्लॉक के बीच कोई अंतर नहीं है जो लॉक ऑब्जेक्ट के रूप में "इस" का उपयोग करके पूरे फ़ंक्शन को कवर करता है। यह सच नहीं है, अंतर बाइट कोड में है जो दोनों स्थितियों में उत्पन्न होगा। सिंक्रनाइज़ किए गए ब्लॉक के उपयोग के मामले में स्थानीय चर आवंटित किया जाना चाहिए जो "यह" का संदर्भ रखता है। और परिणाम के रूप में हमारे पास फ़ंक्शन के लिए थोड़ा बड़ा आकार होगा (प्रासंगिक नहीं यदि आपके पास केवल कुछ फ़ंक्शन हैं)।
अंतर का अधिक विस्तृत विवरण आप यहां पा सकते हैं: http://www.artima.com/insidejvm/ed2/threadsynchP.html
सिंक्रनाइज़ किए गए तरीकों के मामले में, लॉक को एक ऑब्जेक्ट पर अधिग्रहित किया जाएगा। लेकिन अगर आप सिंक्रनाइज़ ब्लॉक के साथ जाते हैं, तो आपके पास एक ऑब्जेक्ट निर्दिष्ट करने का विकल्प होता है, जिस पर ताला अधिग्रहित किया जाएगा।
उदाहरण :
Class Example {
String test = "abc";
// lock will be acquired on String test object.
synchronized (test) {
// do something
}
lock will be acquired on Example Object
public synchronized void testMethod() {
// do some thing
}
}
मुझे पता है कि यह एक पुराना प्रश्न है, लेकिन यहां प्रतिक्रियाओं के अपने त्वरित पढ़ने के साथ, मैंने वास्तव में किसी का उल्लेख नहीं देखा कि कई बार एक synchronizedविधि गलत लॉक हो सकती है ।
जावा कॉन्सेरिटी इन प्रैक्टिस से (पृष्ठ .२):
public class ListHelper<E> {
public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...
public syncrhonized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
उपरोक्त कोड में थ्रेड-सुरक्षित होने का आभास होता है। हालांकि, वास्तव में ऐसा नहीं है। इस मामले में वर्ग के उदाहरण पर ताला प्राप्त किया जाता है। हालांकि, सूची के लिए यह संभव है कि उस विधि का उपयोग न करके किसी अन्य थ्रेड द्वारा संशोधित किया जाए। सही दृष्टिकोण का उपयोग करना होगा
public boolean putIfAbsent(E x) {
synchronized(list) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}
उपरोक्त कोड सूची को संशोधित करने से सिंक्रनाइज़ किए गए ब्लॉक पूरा होने तक सूची को संशोधित करने की कोशिश कर रहे सभी थ्रेड्स को ब्लॉक करेगा ।
Listप्रदर्शन के मुद्दों को जन्म दे सकता है यदि कोड का एक लॉग होता है जिसे आवश्यक रूप से सिंक्रनाइज़ करने की आवश्यकता नहीं होती है
एक व्यावहारिक मामले के रूप में, सिंक्रनाइज़ किए गए ब्लॉकों पर सिंक्रनाइज़ तरीकों का लाभ यह है कि वे अधिक बेवकूफ-प्रतिरोधी हैं; चूँकि आप लॉक करने के लिए एक मनमाना ऑब्जेक्ट नहीं चुन सकते हैं, आप एक स्ट्रिंग शाब्दिक पर लॉक करने या एक उत्परिवर्तित फ़ील्ड की सामग्री पर लॉक करने जैसी बेवकूफ चीजों को करने के लिए सिंक्रनाइज़ किए गए विधि सिंटैक्स का दुरुपयोग नहीं कर सकते जो थ्रेड्स के नीचे से परिवर्तित हो जाते हैं।
दूसरी ओर, सिंक्रनाइज़ किए गए तरीकों से आप लॉक को किसी भी धागे द्वारा अधिग्रहित होने से बचा सकते हैं जो ऑब्जेक्ट का संदर्भ प्राप्त कर सकता है।
तो अपने गाय-दलालों को खुद को चोट पहुंचाने से बचाने के लिए तरीकों पर एक संशोधक के रूप में सिंक्रनाइज़ का उपयोग करना बेहतर है, जबकि निजी अंतिम लॉक ऑब्जेक्ट्स के साथ सिंक्रनाइज़ ब्लॉक का उपयोग करना गाय-विक्रेताओं से अपने स्वयं के कोड की रक्षा करने में बेहतर है।
जावा विनिर्देशन सारांश से: http://www.cs.cornell.edu/andru/javaspec/17.doc.html
सिंक्रनाइज़ स्टेटमेंट (.114.17) किसी ऑब्जेक्ट के संदर्भ की गणना करता है; यह तब उस वस्तु पर लॉक एक्शन करने का प्रयास करता है और तब तक आगे नहीं बढ़ता जब तक कि लॉक एक्शन सफलतापूर्वक पूरा नहीं हो जाता। ...
एक सिंक्रनाइज़ विधि (.48.4.3.5) स्वचालित रूप से एक ताला कार्रवाई करता है जब इसे लागू किया जाता है; इसका शरीर तब तक निष्पादित नहीं होता है जब तक ताला क्रिया सफलतापूर्वक पूरी नहीं हो जाती। यदि विधि एक आवृत्ति विधि है , तो यह उस आवृत्ति से संबद्ध लॉक को लॉक करता है जिसके लिए इसे लागू किया गया था (अर्थात, वह वस्तु जिसे विधि के निकाय के निष्पादन के दौरान इस रूप में जाना जाएगा)। यदि विधि स्थिर है , तो यह उस क्लास ऑब्जेक्ट से जुड़े लॉक को लॉक करता है जो उस क्लास का प्रतिनिधित्व करता है जिसमें विधि परिभाषित है। ...
इन विवरणों के आधार पर, मैं कहूंगा कि अधिकांश पिछले उत्तर सही हैं, और एक स्थिर विधि विशेष रूप से स्थैतिक विधियों के लिए उपयोगी हो सकती है, जहां आपको अन्यथा यह पता लगाना होगा कि "क्लास ऑब्जेक्ट जो उस विधि का प्रतिनिधित्व करता है, उसे कैसे प्राप्त करें" परिभाषित।"
संपादित करें: मैंने मूल रूप से सोचा था कि ये वास्तविक जावा कल्पना के उद्धरण थे। स्पष्ट किया गया कि यह पृष्ठ केवल कल्पना का सारांश / स्पष्टीकरण है
TLDR; न तो synchronizedसंशोधक का उपयोग करें और न ही synchronized(this){...}अभिव्यक्ति का, लेकिन एक निजी वस्तु धारण करने वाला अंतिम उदाहरण क्षेत्र synchronized(myLock){...}कहां myLockहै।
synchronizedविधि घोषणा पर संशोधक का उपयोग करने और synchronized(..){ }विधि निकाय में अभिव्यक्ति के बीच अंतर यह हैं:
synchronizedसंशोधक विधि के हस्ताक्षर पर निर्दिष्ट
synchronized(this) { .... }, औरthisजब स्थैतिक विधि पर घोषित गैर-स्थैतिक विधि या संलग्नक वर्ग पर घोषित किए गए ऑब्जेक्ट का उपयोग करता है ।synchronized(...){...}अभिव्यक्ति की अनुमति देता है
हालांकि, का उपयोग कर synchronizedसंशोधक या synchronized(...) {...}साथ thisताला वस्तु (के रूप में के रूप में synchronized(this) {...}), एक ही नुकसान है। दोनों इसका उपयोग अपने आप में उदाहरण है कि ताला वस्तु पर सिंक्रनाइज़ होने के लिए। यह खतरनाक है क्योंकि केवल वस्तु ही नहीं बल्कि कोई अन्य बाहरी वस्तु / कोड जो उस वस्तु का संदर्भ रखता है, उसे संभावित गंभीर दुष्प्रभावों (प्रदर्शन में गिरावट और गतिरोध ) के साथ सिंक्रनाइज़ेशन लॉक के रूप में भी उपयोग कर सकता है ।
इसलिए सबसे अच्छा अभ्यास न तो synchronizedसंशोधक और न ही synchronized(...)अभिव्यक्ति thisको लॉक ऑब्जेक्ट के रूप में उपयोग करना है, लेकिन इस ऑब्जेक्ट के लिए एक लॉक ऑब्जेक्ट निजी है। उदाहरण के लिए:
public class MyService {
private final lock = new Object();
public void doThis() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
public void doThat() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
}
आप कई लॉक ऑब्जेक्ट का भी उपयोग कर सकते हैं, लेकिन यह सुनिश्चित करने के लिए विशेष सावधानी बरतने की आवश्यकता है कि नेस्टेड का उपयोग करने पर गतिरोध न हो।
public class MyService {
private final lock1 = new Object();
private final lock2 = new Object();
public void doThis() {
synchronized(lock1) {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThat() and doMore().
}
}
public void doThat() {
synchronized(lock1) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doMore() may execute concurrently
}
}
public void doMore() {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doThat() may execute concurrently
}
}
}
मुझे लगता है कि यह प्रश्न थ्रेड सेफ सिंगलटन और लेज़ी इनिशियलाइज़ेशन के बीच डबल चेक लॉकिंग के अंतर के बारे में है । मैं हमेशा इस लेख को संदर्भित करता हूं जब मुझे कुछ विशिष्ट सिंगलटन को लागू करने की आवश्यकता होती है।
खैर, यह एक थ्रेड सेफ सिंगलटन है :
// Java program to create Thread Safe
// Singleton class
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
//synchronized method to control simultaneous access
synchronized public static GFG getInstance()
{
if (instance == null)
{
// if instance is null, initialize
instance = new GFG();
}
return instance;
}
}
पेशेवरों:
आलसी आरंभीकरण संभव है।
यह धागा सुरक्षित है।
विपक्ष:
- getInstance () विधि सिंक्रनाइज़ की गई है, इसलिए यह धीमी कार्यक्षमता का कारण बनती है क्योंकि कई थ्रेड्स इसे एक साथ एक्सेस नहीं कर सकते हैं।
यह डबल चेक लॉकिंग के साथ एक आलसी इनिशियलाइज़ेशन है :
// Java code to explain double check locking
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
public static GFG getInstance()
{
if (instance == null)
{
//synchronized block to remove overhead
synchronized (GFG.class)
{
if(instance==null)
{
// if instance is null, initialize
instance = new GFG();
}
}
}
return instance;
}
}
पेशेवरों:
आलसी आरंभीकरण संभव है।
यह थ्रेड सेफ भी है।
सिंक्रनाइज़ कीवर्ड के कारण प्रदर्शन कम हो गया है।
विपक्ष:
पहली बार, यह प्रदर्शन को प्रभावित कर सकता है।
विपक्ष के रूप में। डबल चेक लॉकिंग विधि का उपयोग करने योग्य है, इसलिए इसे उच्च प्रदर्शन वाले बहु-थ्रेडेड अनुप्रयोगों के लिए उपयोग किया जा सकता है।
अधिक जानकारी के लिए कृपया इस लेख को देखें:
https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/
धागे के साथ सिंक्रनाइज़ करना। 1) एक थ्रेड में सिंक्रनाइज़ (यह) का उपयोग न करें यह काम नहीं करता है। (इस) के साथ सिंक्रनाइज़ करना वर्तमान थ्रेड को लॉकिंग थ्रेड ऑब्जेक्ट के रूप में उपयोग करता है। चूंकि प्रत्येक थ्रेड अन्य थ्रेड्स से स्वतंत्र है, इसलिए सिंक्रनाइज़ेशन का कोई समन्वय नहीं है। 2) कोड के परीक्षण से पता चलता है कि जावा 1.6 में मैक पर विधि सिंक्रनाइज़ेशन काम नहीं करता है। 3) सिंक्रोनाइज़ (LockObj) जहां लॉकऑब्ज सभी थ्रेड्स का एक आम साझा ऑब्जेक्ट है, उस पर सिंक्रोनाइज़ करने से काम चल जाएगा। 4) ReenterantLock.lock () और .unlock () कार्य। इसके लिए जावा ट्यूटोरियल देखें।
निम्न कोड इन बिंदुओं को दर्शाता है। इसमें थ्रेड-सुरक्षित वेक्टर भी शामिल है, जिसे ArrayList के लिए प्रतिस्थापित किया जाएगा, यह दर्शाने के लिए कि वेक्टर में जोड़ने वाले कई थ्रेड्स किसी भी जानकारी को नहीं खोते हैं, जबकि एक ArrayList जानकारी को खो सकता है। ०) करंट रेस की स्थितियों के कारण सूचनाओं का नुकसान होता है ए) वर्तमान लेबल ए लाइन पर टिप्पणी करें, और इसके ऊपर ए लाइन को अनइंस्टॉल करें, फिर चलाएं, विधि डेटा खो देती है लेकिन ऐसा नहीं होना चाहिए। बी) रिवर्स ए, उलटी बी और // अंत ब्लॉक} कदम। फिर परिणाम देखने के लिए चलाएं डेटा C का कोई नुकसान नहीं) बी बाहर टिप्पणी करें, सी। रन चलाएँ, उम्मीद के अनुसार (इस) डेटा खो देता है, पर सिंक्रनाइज़ करना देखें। सभी विविधताओं को पूरा करने का समय नहीं है, आशा है कि यह मदद करता है। यदि यह (या), या विधि सिंक्रनाइज़ेशन काम पर सिंक्रनाइज़ हो रहा है, तो कृपया बताएं कि आपने जावा और ओएस का कौन सा संस्करण परीक्षण किया है। धन्यवाद।
import java.util.*;
/** RaceCondition - Shows that when multiple threads compete for resources
thread one may grab the resource expecting to update a particular
area but is removed from the CPU before finishing. Thread one still
points to that resource. Then thread two grabs that resource and
completes the update. Then thread one gets to complete the update,
which over writes thread two's work.
DEMO: 1) Run as is - see missing counts from race condition, Run severa times, values change
2) Uncomment "synchronized(countLock){ }" - see counts work
Synchronized creates a lock on that block of code, no other threads can
execute code within a block that another thread has a lock.
3) Comment ArrayList, unComment Vector - See no loss in collection
Vectors work like ArrayList, but Vectors are "Thread Safe"
May use this code as long as attribution to the author remains intact.
/mf
*/
public class RaceCondition {
private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
// private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)
private String countLock="lock"; // Object use for locking the raceCount
private int raceCount = 0; // simple add 1 to this counter
private int MAX = 10000; // Do this 10,000 times
private int NUM_THREADS = 100; // Create 100 threads
public static void main(String [] args) {
new RaceCondition();
}
public RaceCondition() {
ArrayList<Thread> arT = new ArrayList<Thread>();
// Create thread objects, add them to an array list
for( int i=0; i<NUM_THREADS; i++){
Thread rt = new RaceThread( ); // i );
arT.add( rt );
}
// Start all object at once.
for( Thread rt : arT ){
rt.start();
}
// Wait for all threads to finish before we can print totals created by threads
for( int i=0; i<NUM_THREADS; i++){
try { arT.get(i).join(); }
catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
}
// All threads finished, print the summary information.
// (Try to print this informaiton without the join loop above)
System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
MAX*NUM_THREADS, raceList.size(), raceCount );
System.out.printf("Array lost %,d. Count lost %,d\n",
MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
} // end RaceCondition constructor
class RaceThread extends Thread {
public void run() {
for ( int i=0; i<MAX; i++){
try {
update( i );
} // These catches show when one thread steps on another's values
catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
catch( OutOfMemoryError oome ) { System.out.print("O"); }
}
}
// so we don't lose counts, need to synchronize on some object, not primitive
// Created "countLock" to show how this can work.
// Comment out the synchronized and ending {, see that we lose counts.
// public synchronized void update(int i){ // use A
public void update(int i){ // remove this when adding A
// synchronized(countLock){ // or B
// synchronized(this){ // or C
raceCount = raceCount + 1;
raceList.add( i ); // use Vector
// } // end block for B or C
} // end update
} // end RaceThread inner class
} // end RaceCondition outter class