CodeGym /Java Blog /यादृच्छिक /एकत्र चांगले: Java आणि थ्रेड वर्ग. भाग II — सिंक्रोनाइझेश...
John Squirrels
पातळी 41
San Francisco

एकत्र चांगले: Java आणि थ्रेड वर्ग. भाग II — सिंक्रोनाइझेशन

यादृच्छिक या ग्रुपमध्ये प्रकाशित केले

परिचय

तर, आम्हाला माहित आहे की Java मध्ये थ्रेड्स आहेत. त्याबद्दल तुम्ही Better together: Java and the Thread class या शीर्षकाच्या पुनरावलोकनात वाचू शकता . भाग I - अंमलबजावणीचे धागे . समांतर काम करण्यासाठी थ्रेड आवश्यक आहेत. यामुळे धागे एकमेकांशी कसा तरी संवाद साधतील याची दाट शक्यता असते. हे कसे घडते आणि आपल्याकडे कोणती मूलभूत साधने आहेत ते पाहू या. एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - १

उत्पन्न

Thread.yield() धक्कादायक आहे आणि क्वचितच वापरले जाते. इंटरनेटवर त्याचे विविध प्रकारे वर्णन केले जाते. थ्रेड्सची काही रांग आहे, ज्यामध्ये धाग्याच्या प्राधान्यक्रमानुसार धागा खाली येईल असे काही लोक लिहित आहेत. इतर लोक लिहितात की थ्रेडची स्थिती "रनिंग" वरून "रन करण्यायोग्य" मध्ये बदलेल (जरी या स्थितींमध्ये फरक नाही, म्हणजे जावा त्यांच्यात फरक करत नाही). वास्तविकता अशी आहे की हे सर्व खूपच कमी प्रसिद्ध आणि तरीही एका अर्थाने सोपे आहे. एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - 2एक बग आहे ( JDK-6416721: (स्पेक थ्रेड) फिक्स Thread.yield() javadoc ) पद्धतीच्या दस्तऐवजीकरणासाठी लॉग केलेला yield(). जर तुम्ही ते वाचले तर हे स्पष्ट होते कीyield()पद्धत प्रत्यक्षात Java थ्रेड शेड्युलरला काही शिफारसी देते की या थ्रेडला कमी अंमलबजावणी वेळ दिला जाऊ शकतो. परंतु प्रत्यक्षात काय होते, म्हणजे शेड्युलर शिफारसीनुसार कार्य करतो की नाही आणि सर्वसाधारणपणे काय करतो, हे JVM च्या अंमलबजावणीवर आणि ऑपरेटिंग सिस्टमवर अवलंबून असते. आणि ते इतर काही घटकांवर देखील अवलंबून असू शकते. जावा भाषा विकसित झाल्यामुळे मल्टीथ्रेडिंगचा पुनर्विचार केला गेला आहे या वस्तुस्थितीमुळे सर्व गोंधळ होण्याची शक्यता आहे. विहंगावलोकन येथे अधिक वाचा: Java Thread.yield() चा संक्षिप्त परिचय .

झोप

एक धागा त्याच्या अंमलबजावणी दरम्यान झोपू शकता. इतर थ्रेड्सशी संवाद साधण्याचा हा सर्वात सोपा प्रकार आहे. जावा व्हर्च्युअल मशीन चालवणाऱ्या ऑपरेटिंग सिस्टीमचा जावा कोडचा स्वतःचा थ्रेड शेड्युलर आहे . कोणता धागा कधी सुरू करायचा ते ठरवते. प्रोग्रामर या शेड्युलरशी थेट Java कोडवरून संवाद साधू शकत नाही, फक्त JVM द्वारे. तो किंवा ती शेड्युलरला धागा थोडावेळ थांबवायला सांगू शकतो, म्हणजे झोपायला ठेवायला. तुम्ही या लेखांमध्ये अधिक वाचू शकता: Thread.sleep() आणि मल्टीथ्रेडिंग कसे कार्य करते . विंडोज ऑपरेटिंग सिस्टीममध्ये थ्रेड कसे कार्य करतात ते देखील तुम्ही तपासू शकता: विंडोज थ्रेडचे अंतर्गत . आणि आता ते आपल्या डोळ्यांनी पाहूया. खालील कोड नावाच्या फाईलमध्ये सेव्ह करा HelloWorldApp.java:

class HelloWorldApp {
    public static void main(String []args) {
        Runnable task = () -> {
            try {
                int secToWait = 1000 * 60;
                Thread.currentThread().sleep(secToWait);
                System.out.println("Woke up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
जसे आपण पाहू शकता, आमच्याकडे काही कार्य आहे जे 60 सेकंदांपर्यंत थांबते, त्यानंतर प्रोग्राम समाप्त होतो. आम्ही " " कमांड वापरून संकलित करतो javac HelloWorldApp.javaआणि नंतर " " वापरून प्रोग्राम चालवतो java HelloWorldApp. वेगळ्या विंडोमध्ये प्रोग्राम सुरू करणे चांगले. उदाहरणार्थ, विंडोजवर, हे असे आहे: start java HelloWorldApp. पीआयडी (प्रोसेस आयडी) मिळविण्यासाठी आम्ही jps कमांड वापरतो, आणि आम्ही " jvisualvm --openpid pid: एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - 3तुम्ही बघू शकता, आमच्या थ्रेडला आता "स्लीपिंग" स्थिती प्राप्त झाली आहे. खरतर, मदत करण्याचा आणखी एक सुंदर मार्ग आहे. आमच्या धाग्याला गोड स्वप्ने आहेत:

try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Woke up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
आम्ही सर्वत्र हाताळत आहोत हे तुमच्या लक्षात आले का InterruptedException? चला समजून घेऊया.

Thread.interrupt()

गोष्ट अशी आहे की धागा वाट पाहत असताना/झोपेत असताना, कोणीतरी व्यत्यय आणू इच्छितो. या प्रकरणात, आम्ही एक हाताळतो InterruptedException. ही Thread.stop()पद्धत कालबाह्य, म्हणजे कालबाह्य आणि अनिष्ट घोषित केल्यानंतर ही यंत्रणा तयार करण्यात आली. कारण असे की जेव्हा stop()पद्धत कॉल केली गेली तेव्हा धागा फक्त "मारला" होता, जो खूप अप्रत्याशित होता. थ्रेड केव्हा थांबवला जाईल हे आम्हाला कळू शकले नाही आणि आम्ही डेटाच्या सुसंगततेची हमी देऊ शकत नाही. कल्पना करा की थ्रेड मारला जात असताना तुम्ही फाइलवर डेटा लिहित आहात. धागा मारण्यापेक्षा जावाच्या निर्मात्यांनी ठरवले की त्यात व्यत्यय आणला पाहिजे हे सांगणे अधिक तर्कसंगत आहे. या माहितीला प्रतिसाद कसा द्यायचा हा धाग्यानेच ठरवायचा विषय आहे. अधिक तपशीलांसाठी, Thread.stop नापसंत का आहे?ओरॅकलच्या वेबसाइटवर. चला एक उदाहरण पाहू:

public static void main(String []args) {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(60);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
या उदाहरणात, आम्ही 60 सेकंद थांबणार नाही. त्याऐवजी, आम्ही त्वरित "व्यत्यय" प्रदर्शित करू. कारण आम्ही interrupt()थ्रेडवर पद्धत म्हटले आहे. ही पद्धत "इंटरप्ट स्टेटस" नावाचा अंतर्गत ध्वज सेट करते. म्हणजेच, प्रत्येक थ्रेडमध्ये एक अंतर्गत ध्वज आहे जो थेट प्रवेशयोग्य नाही. परंतु या ध्वजाशी संवाद साधण्यासाठी आमच्याकडे स्थानिक पद्धती आहेत. पण हा एकमेव मार्ग नाही. एक धागा चालू असू शकतो, कशाचीही वाट पाहत नाही, फक्त क्रिया करत आहे. परंतु इतरांना त्याचे काम विशिष्ट वेळी संपवायचे असेल असा अंदाज असू शकतो. उदाहरणार्थ:

public static void main(String []args) {
	Runnable task = () -> {
		while(!Thread.currentThread().isInterrupted()) {
			// Do some work
		}
		System.out.println("Finished");
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
वरील उदाहरणामध्ये, whileथ्रेड बाहेरून व्यत्यय येईपर्यंत लूप कार्यान्वित केला जाईल. ध्वजासाठी isInterrupted, हे जाणून घेणे महत्त्वाचे आहे की जर आपण एखादे पकडले तर InterruptedException, isInterrupted ध्वज रीसेट होईल आणि नंतर isInterrupted()खोटा परत येईल. थ्रेड क्लासमध्ये स्थिर Thread.interrupted() पद्धत देखील आहे जी फक्त वर्तमान थ्रेडवर लागू होते, परंतु ही पद्धत फ्लॅगला असत्य वर रीसेट करते! थ्रेड इंटरप्शन शीर्षक असलेल्या या प्रकरणामध्ये अधिक वाचा .

सामील व्हा (दुसरा थ्रेड पूर्ण होण्याची प्रतीक्षा करा)

दुसरा धागा संपण्याची वाट पाहण्याचा सोपा प्रकार.

public static void main(String []args) throws InterruptedException {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.join();
	System.out.println("Finished");
}
या उदाहरणात, नवीन थ्रेड 5 सेकंद झोपेल. त्याच वेळी, मुख्य धागा झोपलेला धागा जागे होईपर्यंत आणि त्याचे कार्य पूर्ण होईपर्यंत प्रतीक्षा करेल. जर तुम्ही JVisualVM मधील थ्रेडची स्थिती पाहिली तर ते असे दिसेल: एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - 4मॉनिटरिंग टूल्सबद्दल धन्यवाद, तुम्ही थ्रेडमध्ये काय चालले आहे ते पाहू शकता. पद्धत joinअगदी सोपी आहे, कारण ही फक्त Java कोड असलेली एक पद्धत आहे जी wait()ज्या थ्रेडवर म्हणतात तो जिवंत आहे तोपर्यंत कार्यान्वित होते. धागा मरताच (जेव्हा त्याचे काम पूर्ण होते), प्रतीक्षा व्यत्यय आणली जाते. आणि हीच पद्धतीची जादू आहे join(). तर, चला सर्वात मनोरंजक गोष्टीकडे जाऊया.

मॉनिटर

मल्टीथ्रेडिंगमध्ये मॉनिटरची संकल्पना समाविष्ट आहे. मॉनिटर हा शब्द इंग्रजीत 16व्या शतकातील लॅटिन भाषेत येतो आणि याचा अर्थ "प्रक्रियेचे निरीक्षण, तपासणी किंवा सतत रेकॉर्ड ठेवण्यासाठी वापरलेले साधन किंवा उपकरण" असा होतो. या लेखाच्या संदर्भात, आम्ही मूलभूत गोष्टी कव्हर करण्याचा प्रयत्न करू. ज्यांना तपशील हवे आहेत, कृपया लिंक केलेल्या सामग्रीमध्ये जा. आम्ही जावा लँग्वेज स्पेसिफिकेशन (JLS): 17.1 सह आमचा प्रवास सुरू करतो. सिंक्रोनाइझेशन . हे खालील म्हणते: एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - 5असे दिसून आले की जावा थ्रेड्स दरम्यान सिंक्रोनाइझेशनसाठी "मॉनिटर" यंत्रणा वापरते. मॉनिटर प्रत्येक ऑब्जेक्टशी संबंधित असतो आणि थ्रेड्स ते मिळवू शकतात lock()किंवा सोबत सोडू शकतात unlock(). पुढे, आम्ही ओरॅकल वेबसाइटवर ट्यूटोरियल शोधू: आंतरिक लॉक आणि सिंक्रोनाइझेशन. हे ट्यूटोरियल म्हणते की Java चे सिंक्रोनाइझेशन एका अंतर्गत घटकाभोवती तयार केले गेले आहे ज्याला आंतरिक लॉक किंवा मॉनिटर लॉक म्हणतात . या लॉकला सहसा " मॉनिटर " म्हटले जाते. आपण हे देखील पाहतो की Java मधील प्रत्येक ऑब्जेक्टला त्याच्याशी संबंधित एक आंतरिक लॉक आहे. तुम्ही Java - Intrinsic Locks आणि Synchronization वाचू शकता . पुढे जावामधील एखादी वस्तू मॉनिटरशी कशी जोडली जाऊ शकते हे समजून घेणे महत्त्वाचे आहे. Java मध्ये, प्रत्येक ऑब्जेक्टमध्ये एक शीर्षलेख असतो जो कोडमधील प्रोग्रामरसाठी उपलब्ध नसलेला अंतर्गत मेटाडेटा संग्रहित करतो, परंतु व्हर्च्युअल मशीनला ऑब्जेक्टसह योग्यरित्या कार्य करणे आवश्यक असते. ऑब्जेक्ट हेडरमध्ये "मार्क शब्द" समाविष्ट आहे, जो यासारखा दिसतो: एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - 6

https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf

येथे एक JavaWorld लेख आहे जो खूप उपयुक्त आहे: Java आभासी मशीन थ्रेड सिंक्रोनाइझेशन कसे करते . हा लेख JDK बग-ट्रॅकिंग सिस्टममधील खालील समस्येच्या "सारांश" विभागातील वर्णनासह एकत्र केला पाहिजे: JDK-8183909 . तुम्ही तीच गोष्ट इथे वाचू शकता: JEP-8183909 . तर, Java मध्ये, मॉनिटर एखाद्या ऑब्जेक्टशी संबंधित असतो आणि जेव्हा थ्रेड लॉक मिळवण्याचा (किंवा मिळवण्याचा) प्रयत्न करतो तेव्हा थ्रेड ब्लॉक करण्यासाठी वापरला जातो. येथे सर्वात सोपे उदाहरण आहे:

public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
येथे, सध्याचा थ्रेड (ज्या कोडच्या या ओळी कार्यान्वित केल्या जातात तो) synchronizedयाशी संबंधित मॉनिटर वापरण्याचा प्रयत्न करण्यासाठी कीवर्ड वापरतो.object"\लॉक मिळवण्यासाठी/प्राप्त करण्यासाठी व्हेरिएबल. जर इतर कोणीही मॉनिटरसाठी विवाद करत नसेल (म्हणजे समान ऑब्जेक्ट वापरून इतर कोणीही सिंक्रोनाइझ केलेला कोड चालवत नसेल), तर Java "बायस्ड लॉकिंग" नावाचे ऑप्टिमायझेशन करण्याचा प्रयत्न करू शकते. संबंधित टॅग आणि मॉनिटरचे लॉक कोणत्या थ्रेडच्या मालकीचे आहे याची नोंद ऑब्जेक्ट हेडरमधील चिन्ह शब्दात जोडली जाते. हे मॉनिटर लॉक करण्यासाठी आवश्यक ओव्हरहेड कमी करते. जर मॉनिटर पूर्वी दुसर्या थ्रेडच्या मालकीचा असेल तर असे लॉकिंग पुरेसे नाही. JVM पुढील प्रकारच्या लॉकिंगवर स्विच करते: "मूलभूत लॉकिंग". हे तुलना-आणि-स्वॅप (CAS) ऑपरेशन्स वापरते. इतकेच काय, ऑब्जेक्ट हेडरचा मार्क शब्द यापुढे मार्क शब्द संग्रहित करत नाही, तर तो कुठे संग्रहित केला आहे याचा संदर्भ देतो आणि टॅग बदलतो जेणेकरून JVM ला समजेल की आपण मूलभूत लॉकिंग वापरत आहोत. जर एकाधिक थ्रेड्स मॉनिटरसाठी स्पर्धा (वाद) करत असतील (एकाने लॉक मिळवला असेल आणि दुसरा लॉक रिलीझ होण्याची वाट पाहत असेल), तर मार्क शब्दातील टॅग बदलतो आणि मार्क शब्द आता मॉनिटरचा संदर्भ संग्रहित करतो. ऑब्जेक्ट म्हणून - JVM चे काही अंतर्गत अस्तित्व. JDK Enchancement Proposal (JEP) मध्ये म्हटल्याप्रमाणे, या परिस्थितीला ही संस्था संग्रहित करण्यासाठी मेमरीच्या नेटिव्ह हीप भागात जागा आवश्यक आहे. या अंतर्गत घटकाच्या मेमरी स्थानाचा संदर्भ ऑब्जेक्ट हेडरच्या मार्क शब्दामध्ये संग्रहित केला जाईल. अशाप्रकारे, मॉनिटर ही खरोखरच एकाधिक थ्रेड्समध्ये सामायिक केलेल्या संसाधनांमध्ये प्रवेश समक्रमित करण्यासाठी एक यंत्रणा आहे. JVM या यंत्रणेच्या अनेक अंमलबजावणी दरम्यान स्विच करते. तर, साधेपणासाठी, मॉनिटरबद्दल बोलत असताना, आम्ही प्रत्यक्षात लॉकबद्दल बोलत आहोत. आणि एक सेकंद लॉक रिलीझ होण्याची वाट पाहत आहे), त्यानंतर मार्क शब्दातील टॅग बदलतो आणि मार्क शब्द आता मॉनिटरचा संदर्भ ऑब्जेक्ट म्हणून संग्रहित करतो — JVM चे काही अंतर्गत अस्तित्व. JDK Enchancement Proposal (JEP) मध्ये म्हटल्याप्रमाणे, या परिस्थितीला ही संस्था संग्रहित करण्यासाठी मेमरीच्या नेटिव्ह हीप भागात जागा आवश्यक आहे. या अंतर्गत घटकाच्या मेमरी स्थानाचा संदर्भ ऑब्जेक्ट हेडरच्या मार्क शब्दामध्ये संग्रहित केला जाईल. अशाप्रकारे, मॉनिटर ही खरोखरच एकाधिक थ्रेड्समध्ये सामायिक केलेल्या संसाधनांमध्ये प्रवेश समक्रमित करण्यासाठी एक यंत्रणा आहे. JVM या यंत्रणेच्या अनेक अंमलबजावणी दरम्यान स्विच करते. तर, साधेपणासाठी, मॉनिटरबद्दल बोलत असताना, आम्ही प्रत्यक्षात लॉकबद्दल बोलत आहोत. आणि एक सेकंद लॉक रिलीझ होण्याची वाट पाहत आहे), त्यानंतर मार्क शब्दातील टॅग बदलतो आणि मार्क शब्द आता मॉनिटरचा संदर्भ ऑब्जेक्ट म्हणून संग्रहित करतो — JVM चे काही अंतर्गत अस्तित्व. JDK Enchancement Proposal (JEP) मध्ये म्हटल्याप्रमाणे, या परिस्थितीला ही संस्था संग्रहित करण्यासाठी मेमरीच्या नेटिव्ह हीप भागात जागा आवश्यक आहे. या अंतर्गत घटकाच्या मेमरी स्थानाचा संदर्भ ऑब्जेक्ट हेडरच्या मार्क शब्दामध्ये संग्रहित केला जाईल. अशाप्रकारे, मॉनिटर ही खरोखरच एकाधिक थ्रेड्समध्ये सामायिक केलेल्या संसाधनांमध्ये प्रवेश समक्रमित करण्यासाठी एक यंत्रणा आहे. JVM या यंत्रणेच्या अनेक अंमलबजावणी दरम्यान स्विच करते. तर, साधेपणासाठी, मॉनिटरबद्दल बोलत असताना, आम्ही प्रत्यक्षात लॉकबद्दल बोलत आहोत. आणि मार्क शब्द आता मॉनिटरचा संदर्भ ऑब्जेक्ट म्हणून संग्रहित करतो — JVM चे काही अंतर्गत अस्तित्व. JDK Enchancement Proposal (JEP) मध्ये म्हटल्याप्रमाणे, या परिस्थितीला ही संस्था संग्रहित करण्यासाठी मेमरीच्या नेटिव्ह हीप भागात जागा आवश्यक आहे. या अंतर्गत घटकाच्या मेमरी स्थानाचा संदर्भ ऑब्जेक्ट हेडरच्या मार्क शब्दामध्ये संग्रहित केला जाईल. अशाप्रकारे, मॉनिटर ही खरोखरच एकाधिक थ्रेड्समध्ये सामायिक केलेल्या संसाधनांमध्ये प्रवेश समक्रमित करण्यासाठी एक यंत्रणा आहे. JVM या यंत्रणेच्या अनेक अंमलबजावणी दरम्यान स्विच करते. तर, साधेपणासाठी, मॉनिटरबद्दल बोलत असताना, आम्ही प्रत्यक्षात लॉकबद्दल बोलत आहोत. आणि मार्क शब्द आता मॉनिटरचा संदर्भ ऑब्जेक्ट म्हणून संग्रहित करतो — JVM चे काही अंतर्गत अस्तित्व. JDK Enchancement Proposal (JEP) मध्ये म्हटल्याप्रमाणे, या परिस्थितीला ही संस्था संग्रहित करण्यासाठी मेमरीच्या नेटिव्ह हीप भागात जागा आवश्यक आहे. या अंतर्गत घटकाच्या मेमरी स्थानाचा संदर्भ ऑब्जेक्ट हेडरच्या मार्क शब्दामध्ये संग्रहित केला जाईल. अशाप्रकारे, मॉनिटर ही खरोखरच एकाधिक थ्रेड्समध्ये सामायिक केलेल्या संसाधनांमध्ये प्रवेश समक्रमित करण्यासाठी एक यंत्रणा आहे. JVM या यंत्रणेच्या अनेक अंमलबजावणी दरम्यान स्विच करते. तर, साधेपणासाठी, मॉनिटरबद्दल बोलत असताना, आम्ही प्रत्यक्षात लॉकबद्दल बोलत आहोत. या अंतर्गत घटकाच्या मेमरी स्थानाचा संदर्भ ऑब्जेक्ट हेडरच्या मार्क शब्दामध्ये संग्रहित केला जाईल. अशाप्रकारे, मॉनिटर ही खरोखरच एकाधिक थ्रेड्समध्ये सामायिक केलेल्या संसाधनांमध्ये प्रवेश समक्रमित करण्यासाठी एक यंत्रणा आहे. JVM या यंत्रणेच्या अनेक अंमलबजावणी दरम्यान स्विच करते. तर, साधेपणासाठी, मॉनिटरबद्दल बोलत असताना, आम्ही प्रत्यक्षात लॉकबद्दल बोलत आहोत. या अंतर्गत घटकाच्या मेमरी स्थानाचा संदर्भ ऑब्जेक्ट हेडरच्या मार्क शब्दामध्ये संग्रहित केला जाईल. अशाप्रकारे, मॉनिटर ही खरोखरच एकाधिक थ्रेड्समध्ये सामायिक केलेल्या संसाधनांमध्ये प्रवेश समक्रमित करण्यासाठी एक यंत्रणा आहे. JVM या यंत्रणेच्या अनेक अंमलबजावणी दरम्यान स्विच करते. तर, साधेपणासाठी, मॉनिटरबद्दल बोलत असताना, आम्ही प्रत्यक्षात लॉकबद्दल बोलत आहोत. एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - 7

सिंक्रोनाइझ (लॉकची वाट पाहत आहे)

आपण आधी पाहिल्याप्रमाणे, "सिंक्रोनाइझ्ड ब्लॉक" (किंवा "गंभीर विभाग") ही संकल्पना मॉनिटरच्या संकल्पनेशी जवळून संबंधित आहे. एक उदाहरण पहा:

public static void main(String[] args) throws InterruptedException {
	Object lock = new Object();

	Runnable task = () -> {
		synchronized(lock) {
			System.out.println("thread");
		}
	};

	Thread th1 = new Thread(task);
	th1.start();
	synchronized(lock) {
		for (int i = 0; i < 8; i++) {
			Thread.currentThread().sleep(1000);
			System.out.print(" " + i);
		}
		System.out.println(" ...");
	}
}
येथे, मुख्य थ्रेड प्रथम टास्क ऑब्जेक्टला नवीन थ्रेडवर पास करतो आणि नंतर ताबडतोब लॉक मिळवतो आणि त्याच्यासह दीर्घ ऑपरेशन करतो (8 सेकंद). या सर्व वेळी, कार्य पुढे जाण्यास अक्षम आहे, कारण ते synchronizedब्लॉकमध्ये प्रवेश करू शकत नाही, कारण लॉक आधीच अधिग्रहित केले आहे. जर थ्रेड लॉक मिळवू शकत नसेल, तर तो मॉनिटरची प्रतीक्षा करेल. लॉक मिळताच त्याची अंमलबजावणी सुरू राहील. जेव्हा थ्रेड मॉनिटरमधून बाहेर पडतो तेव्हा तो लॉक सोडतो. JVisualVM मध्ये, हे असे दिसते: एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - 8जसे आपण JVisualVM मध्ये पाहू शकता, स्थिती "मॉनिटर" आहे, म्हणजे थ्रेड अवरोधित आहे आणि मॉनिटर घेऊ शकत नाही. तुम्ही थ्रेडची स्थिती निर्धारित करण्यासाठी कोड देखील वापरू शकता, परंतु अशा प्रकारे निर्धारित केलेल्या स्थितीची नावे JVisualVM मध्ये वापरलेल्या नावांशी जुळत नाहीत, जरी ते समान आहेत. या प्रकरणात, दth1.getState()फॉर लूपमधील स्टेटमेंट BLOCKED परत करेल , कारण जोपर्यंत लूप चालू आहे, lockऑब्जेक्टचा मॉनिटर थ्रेडने व्यापलेला असतो main, आणि th1थ्रेड ब्लॉक केला जातो आणि लॉक रिलीझ होईपर्यंत पुढे जाऊ शकत नाही. सिंक्रोनाइझ केलेल्या ब्लॉक्स व्यतिरिक्त, संपूर्ण पद्धत सिंक्रोनाइझ केली जाऊ शकते. उदाहरणार्थ, वर्गातील एक पद्धत येथे आहे HashTable:

public synchronized int size() {
	return count;
}
ही पद्धत कोणत्याही वेळी केवळ एका थ्रेडद्वारे कार्यान्वित केली जाईल. आम्हाला खरोखर लॉकची गरज आहे का? होय, आम्हाला त्याची गरज आहे. उदाहरण पद्धतींच्या बाबतीत, "ही" ऑब्जेक्ट (वर्तमान ऑब्जेक्ट) लॉक म्हणून कार्य करते. या विषयावर येथे एक मनोरंजक चर्चा आहे: सिंक्रोनाइझ ब्लॉक ऐवजी सिंक्रोनाइझ केलेली पद्धत वापरण्याचा फायदा आहे का? . जर पद्धत स्टॅटिक असेल, तर लॉक "हा" ऑब्जेक्ट नसेल (कारण स्टॅटिक पद्धतीसाठी "हा" ऑब्जेक्ट नाही), तर क्लास ऑब्जेक्ट असेल (उदाहरणार्थ, ) Integer.class.

प्रतीक्षा करा (मॉनिटरची वाट पहात आहे). notify() आणि notifyAll() पद्धती

थ्रेड क्लासमध्ये आणखी एक प्रतीक्षा पद्धत आहे जी मॉनिटरशी संबंधित आहे. विपरीत sleep()आणि join(), या पद्धतीला फक्त म्हटले जाऊ शकत नाही. त्याचे नाव आहे wait(). waitमॉनिटरशी संबंधित ऑब्जेक्टवर ही पद्धत कॉल केली जाते ज्याची आपण प्रतीक्षा करू इच्छितो. चला एक उदाहरण पाहू:

public static void main(String []args) throws InterruptedException {
	    Object lock = new Object();
	    // The task object will wait until it is notified via lock
	    Runnable task = () -> {
	        synchronized(lock) {
	            try {
	                lock.wait();
	            } catch(InterruptedException e) {
	                System.out.println("interrupted");
	            }
	        }
	        // After we are notified, we will wait until we can acquire the lock
	        System.out.println("thread");
	    };
	    Thread taskThread = new Thread(task);
	    taskThread.start();
        // We sleep. Then we acquire the lock, notify, and release the lock
	    Thread.currentThread().sleep(3000);
	    System.out.println("main");
	    synchronized(lock) {
	        lock.notify();
	    }
}
JVisualVM मध्ये, हे असे दिसते: एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - १०हे कसे कार्य करते हे समजून घेण्यासाठी, लक्षात ठेवा की wait()आणि notify()पद्धती यांच्याशी संबंधित आहेत java.lang.Object. थ्रेडशी संबंधित पद्धती वर्गात आहेत हे विचित्र वाटू शकते Object. पण त्याचे कारण आता समोर आले आहे. तुम्हाला आठवत असेल की Java मधील प्रत्येक ऑब्जेक्टला हेडर असते. हेडरमध्ये मॉनिटरबद्दलच्या माहितीसह, म्हणजे लॉकची स्थिती यासह विविध हाउसकीपिंग माहिती असते. लक्षात ठेवा, प्रत्येक ऑब्जेक्ट किंवा क्लासचे उदाहरण, JVM मधील अंतर्गत घटकाशी संबंधित आहे, ज्याला आंतरिक लॉक किंवा मॉनिटर म्हणतात. वरील उदाहरणामध्ये, टास्क ऑब्जेक्टसाठी कोड सूचित करतो की आम्ही ऑब्जेक्टशी संबंधित मॉनिटरसाठी सिंक्रोनाइझ केलेला ब्लॉक प्रविष्ट करतो lock. जर आम्ही या मॉनिटरसाठी लॉक मिळवण्यात यशस्वी झालो, तरwait()असे म्हणतात. कार्य अंमलात आणणारा थ्रेड ऑब्जेक्टचा मॉनिटर रिलीज करेल , परंतु ऑब्जेक्टच्या मॉनिटरकडून lockसूचनेची वाट पाहत असलेल्या थ्रेडच्या रांगेत प्रवेश करेल . lockथ्रेड्सच्या या रांगेला WAIT SET म्हणतात, जो त्याचा उद्देश अधिक योग्यरित्या प्रतिबिंबित करतो. म्हणजेच, हे रांगेपेक्षा एक संच अधिक आहे. थ्रेड mainटास्क ऑब्जेक्टसह एक नवीन थ्रेड तयार करतो, तो सुरू करतो आणि 3 सेकंद प्रतीक्षा करतो. यामुळे नवीन थ्रेड थ्रेडच्या आधी लॉक प्राप्त करण्यास सक्षम असेल mainआणि मॉनिटरच्या रांगेत जाण्याची दाट शक्यता आहे. त्यानंतर, mainथ्रेड स्वतः lockऑब्जेक्टच्या सिंक्रोनाइझ ब्लॉकमध्ये प्रवेश करतो आणि मॉनिटर वापरून थ्रेड सूचना करतो. सूचना पाठवल्यानंतर, mainथ्रेड रिलीझ करतोlockऑब्जेक्टचा मॉनिटर, आणि नवीन थ्रेड, जो पूर्वी lockऑब्जेक्टचा मॉनिटर रिलीज होण्याची वाट पाहत होता, त्याची अंमलबजावणी सुरू ठेवते. notify()केवळ एका थ्रेडवर ( ) किंवा एकाच वेळी रांगेतील सर्व थ्रेडवर सूचना पाठवणे शक्य आहे ( notifyAll()). येथे अधिक वाचा: Java मधील notify() आणि notifyAll() मधील फरक . हे लक्षात घेणे महत्त्वाचे आहे की सूचना ऑर्डर JVM ची अंमलबजावणी कशी केली जाते यावर अवलंबून असते. येथे अधिक वाचा: अधिसूचना आणि सर्व सूचित करून उपासमार कशी सोडवायची? . ऑब्जेक्ट निर्दिष्ट केल्याशिवाय सिंक्रोनाइझेशन केले जाऊ शकते. कोडच्या एका ब्लॉकऐवजी संपूर्ण पद्धत सिंक्रोनाइझ केल्यावर तुम्ही हे करू शकता. उदाहरणार्थ, स्टॅटिक पद्धतींसाठी, लॉक क्लास ऑब्जेक्ट असेल (याद्वारे प्राप्त .class):

public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
लॉक वापरण्याच्या बाबतीत, दोन्ही पद्धती समान आहेत. जर पद्धत स्थिर नसेल, तर सिंक्रोनाइझेशन चालू वापरून केले जाईल instance, म्हणजेच वापरून this. तसे, आम्ही आधी सांगितले होते की तुम्ही getState()थ्रेडची स्थिती मिळविण्यासाठी पद्धत वापरू शकता. उदाहरणार्थ, मॉनिटरची वाट पाहत असलेल्या रांगेतील थ्रेडसाठी, पद्धतीने wait()कालबाह्य निर्दिष्ट केल्यास, स्थिती WAITING किंवा TIMED_WAITING असेल. एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - 11

https://stackoverflow.com/questions/36425942/what-is-the-lifecycle-of-thread-in-java

धागा जीवन चक्र

त्याच्या आयुष्यादरम्यान, धाग्याची स्थिती बदलते. खरं तर, या बदलांमध्ये धाग्याचे जीवन चक्र असते. धागा तयार होताच त्याची स्थिती नवीन असते. या स्थितीत, नवीन थ्रेड अद्याप चालू नाही आणि Java थ्रेड शेड्यूलरला अद्याप याबद्दल काहीही माहिती नाही. थ्रेड शेड्युलरला थ्रेडबद्दल जाणून घेण्यासाठी, तुम्ही thread.start()पद्धत कॉल करणे आवश्यक आहे. मग थ्रेड RUNNABLE स्थितीत बदलेल. इंटरनेटमध्ये "रन करण्यायोग्य" आणि "रनिंग" स्थितींमध्ये फरक करणारे बरेच चुकीचे आकृत्या आहेत. पण ही एक चूक आहे, कारण जावा "रेडी टू वर्क" (धावण्यायोग्य) आणि "कार्यरत" (धावणे) यात फरक करत नाही. जेव्हा थ्रेड जिवंत असतो परंतु सक्रिय नसतो (चालण्यायोग्य नसतो), तो दोनपैकी एका स्थितीत असतो:
  • अवरोधित — गंभीर विभागात प्रवेश करण्यासाठी प्रतीक्षा करत आहे, म्हणजे ब्लॉक synchronized.
  • प्रतीक्षा - काही अट पूर्ण करण्यासाठी दुसर्या धाग्याची वाट पाहत आहे.
जर अट समाधानी असेल, तर थ्रेड शेड्यूलर थ्रेड सुरू करतो. जर थ्रेड निर्दिष्ट वेळेपर्यंत वाट पाहत असेल, तर त्याची स्थिती TIMED_WAITING आहे. जर थ्रेड यापुढे चालू नसेल (तो पूर्ण झाला असेल किंवा अपवाद टाकला गेला असेल), तर तो संपुष्टात आलेल्या स्थितीत प्रवेश करतो. थ्रेडची स्थिती शोधण्यासाठी, getState()पद्धत वापरा. थ्रेड्समध्ये एक isAlive()पद्धत देखील असते, जी थ्रेड संपुष्टात न आल्यास खरी परत येते.

लॉक सपोर्ट आणि थ्रेड पार्किंग

Java 1.6 च्या सुरुवातीस, लॉकसपोर्ट नावाची एक मनोरंजक यंत्रणा दिसली. एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - १२हा वर्ग वापरणाऱ्या प्रत्येक थ्रेडशी "परमिट" जोडतो. park()परमिट उपलब्ध असल्यास, प्रक्रियेत परमिट वापरून, पद्धतीवर कॉल लगेच येतो. अन्यथा, ते अवरोधित करते. unparkअद्याप उपलब्ध नसल्यास, पद्धत कॉल केल्याने परमिट उपलब्ध होते. फक्त 1 परमिट आहे. जावा दस्तऐवजीकरण LockSupportवर्गाचा संदर्भ देते Semaphore. चला एक साधे उदाहरण पाहू:

import java.util.concurrent.Semaphore;
public class HelloWorldApp{
    
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(0);
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            // Request the permit and wait until we get it
            e.printStackTrace();
        }
        System.out.println("Hello, World!");
    }
}
हा कोड नेहमी प्रतीक्षा करेल, कारण आता सेमफोरकडे 0 परवानग्या आहेत. आणि acquire()कोडमध्ये कॉल केल्यावर (म्हणजे परमिटची विनंती करा), थ्रेड परमिट मिळेपर्यंत थांबतो. आम्ही वाट पाहत असल्याने, आम्हाला हाताळले पाहिजे InterruptedException. विशेष म्हणजे, सेमाफोरला स्वतंत्र थ्रेड स्टेट मिळते. आपण JVisualVM मध्ये पाहिल्यास, आपल्याला दिसेल की राज्य "प्रतीक्षा" नाही तर "पार्क" आहे. एकत्र चांगले: Java आणि थ्रेड वर्ग.  भाग II — सिंक्रोनाइझेशन - १३आणखी एक उदाहरण पाहू:

public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            // Park the current thread
            System.err.println("Will be Parked");
            LockSupport.park();
            // As soon as we are unparked, we will start to act
            System.err.println("Unparked");
        };
        Thread th = new Thread(task);
        th.start();
        Thread.currentThread().sleep(2000);
        System.err.println("Thread state: " + th.getState());
        
        LockSupport.unpark(th);
        Thread.currentThread().sleep(2000);
}
थ्रेडची स्थिती प्रतीक्षारत असेल, परंतु JVisualVM waitकीवर्ड synchronizedआणि parkक्लासमध्ये फरक करते LockSupport. हे LockSupportइतके महत्त्वाचे का आहे? आम्ही पुन्हा Java दस्तऐवजीकरणाकडे वळतो आणि प्रतीक्षा थ्रेड स्थिती पाहतो. जसे आपण पाहू शकता, त्यात प्रवेश करण्याचे फक्त तीन मार्ग आहेत. त्यापैकी दोन मार्ग आहेत wait()आणि join(). आणि तिसरा आहे LockSupport. जावा मध्ये, लॉक LockSupporटी वर देखील तयार केले जाऊ शकतात आणि उच्च-स्तरीय साधने देऊ शकतात. चला एक वापरण्याचा प्रयत्न करूया. उदाहरणार्थ, पहा ReentrantLock:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld{

    public static void main(String []args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Runnable task = () -> {
            lock.lock();
            System.out.println("Thread");
            lock.unlock();
        };
        lock.lock();

        Thread th = new Thread(task);
        th.start();
        System.out.println("main");
        Thread.currentThread().sleep(2000);
        lock.unlock();
    }
}
मागील उदाहरणांप्रमाणेच येथे सर्व काही सोपे आहे. ऑब्जेक्ट lockकोणीतरी सामायिक संसाधन सोडण्याची प्रतीक्षा करते. mainआम्ही JVisualVM मध्ये पाहिल्यास, आम्हाला दिसेल की जोपर्यंत थ्रेड लॉक सोडत नाही तोपर्यंत नवीन थ्रेड पार्क केला जाईल . तुम्ही येथे लॉक्सबद्दल अधिक वाचू शकता: Java 8 StampedLocks vs. ReadWriteLocks आणि Synchronized and Lock API Java मध्ये. लॉक कसे अंमलात आणले जातात हे अधिक चांगल्या प्रकारे समजून घेण्यासाठी, या लेखातील Phaser बद्दल वाचणे उपयुक्त आहे: Java Phaser चे मार्गदर्शक . आणि विविध सिंक्रोनायझर्सबद्दल बोलताना, तुम्ही Java Synchronizers वरील DZone लेख वाचला पाहिजे.

निष्कर्ष

या पुनरावलोकनात, आम्ही जावामध्ये थ्रेड्स परस्परसंवादाचे मुख्य मार्ग तपासले. अतिरिक्त साहित्य: एकत्र चांगले: Java आणि थ्रेड वर्ग. भाग I — अंमलबजावणीचे धागे एकत्र चांगले: Java आणि थ्रेड क्लास. भाग तिसरा — परस्परसंवाद चांगले एकत्र: Java आणि थ्रेड वर्ग. भाग IV — कॉल करण्यायोग्य, भविष्य आणि मित्र एकत्र चांगले: Java आणि थ्रेड वर्ग. भाग V — एक्झिक्युटर, थ्रेडपूल, फोर्क/जॉईन बेटर एकत्र: Java आणि थ्रेड क्लास. भाग VI - आग दूर!
टिप्पण्या
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION