CodeGym /Java Blog /अनियमित /बेहतर एक साथ: जावा और थ्रेड क्लास। भाग VI - आग बुझाओ!
John Squirrels
स्तर 41
San Francisco

बेहतर एक साथ: जावा और थ्रेड क्लास। भाग VI - आग बुझाओ!

अनियमित ग्रुप में प्रकाशित

परिचय

थ्रेड्स एक दिलचस्प चीज है। पिछली समीक्षाओं में, हमने मल्टीथ्रेडिंग को लागू करने के लिए कुछ उपलब्ध टूल देखे। आइए देखें कि हम और कौन सी दिलचस्प चीजें कर सकते हैं। इस बिंदु पर, हम बहुत कुछ जानते हैं। उदाहरण के लिए, " बेहतर एक साथ: जावा और थ्रेड वर्ग। भाग I - निष्पादन के धागे ", हम जानते हैं कि थ्रेड वर्ग निष्पादन के धागे का प्रतिनिधित्व करता है। हम जानते हैं कि एक थ्रेड कुछ कार्य करता है। यदि हम चाहते हैं कि हमारे कार्य सक्षम हों run, तो हमें धागे को चिह्नित करना चाहिए Runnableबेहतर एक साथ: जावा और थ्रेड क्लास।  भाग VI - आग बुझाओ!  - 1याद रखने के लिए, हम Tutorialspoint Online Java Compiler का उपयोग कर सकते हैं :

public static void main(String[] args){
	Runnable task = () -> {
 		Thread thread = Thread.currentThread();
		System.out.println("Hello from " + thread.getName());
	};
	Thread thread = new Thread(task);
	thread.start();
}
हम यह भी जानते हैं कि हमारे पास ताला नाम की कोई चीज होती है। हमने इसके बारे में " बेहतर एक साथ: जावा और थ्रेड क्लास। भाग II - सिंक्रोनाइज़ेशन में सीखा । यदि एक थ्रेड लॉक प्राप्त करता है, तो लॉक प्राप्त करने का प्रयास करने वाला दूसरा थ्रेड लॉक के रिलीज़ होने की प्रतीक्षा करने के लिए मजबूर होगा:

import java.util.concurrent.locks.*;

public class HelloWorld{
	public static void main(String []args){
		Lock lock = new ReentrantLock();
		Runnable task = () -> {
			lock.lock();
			Thread thread = Thread.currentThread();
			System.out.println("Hello from " + thread.getName());
			lock.unlock();
		};
		Thread thread = new Thread(task);
		thread.start();
	}
}
मुझे लगता है कि यह बात करने का समय है कि हम और कौन सी दिलचस्प चीजें कर सकते हैं।

सेमाफोरस

एक साथ कितने धागे चल सकते हैं इसे नियंत्रित करने का सबसे आसान तरीका एक सेमाफोर है। यह रेलवे सिग्नल की तरह है। ग्रीन का मतलब है आगे बढ़ें। लाल का मतलब है रुको। सेमाफोर से किसकी प्रतीक्षा करें? पहुँच। पहुँच प्राप्त करने के लिए, हमें इसे प्राप्त करना होगा। और जब पहुंच की आवश्यकता नहीं रह जाती है, तो हमें इसे देना चाहिए या इसे जारी करना चाहिए। आइए देखें कि यह कैसे काम करता है। हमें java.util.concurrent.Semaphoreकक्षा आयात करने की जरूरत है। उदाहरण:

public static void main(String[] args) throws InterruptedException {
	Semaphore semaphore = new Semaphore(0);
	Runnable task = () -> {
		try {
			semaphore.acquire();
			System.out.println("Finished");
			semaphore.release();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	new Thread(task).start();
	Thread.sleep(5000);
	semaphore.release(1);
}
जैसा कि आप देख सकते हैं, ये ऑपरेशन (अधिग्रहण और रिलीज़) हमें यह समझने में मदद करते हैं कि एक सेमाफोर कैसे काम करता है। सबसे महत्वपूर्ण बात यह है कि यदि हमें पहुँच प्राप्त करनी है, तो सेमाफोर में सकारात्मक संख्या में परमिट होने चाहिए। इस गिनती को ऋणात्मक संख्या में प्रारंभ किया जा सकता है। और हम 1 से अधिक परमिट का अनुरोध (अधिग्रहण) कर सकते हैं।

काउंटडाउन लैच

अगला तंत्र है CountDownLatch। अप्रत्याशित रूप से, यह उलटी गिनती के साथ कुंडी है। java.util.concurrent.CountDownLatchयहां हमें वर्ग के लिए उपयुक्त आयात विवरण की आवश्यकता है । यह एक फुट रेस की तरह है, जहां हर कोई स्टार्टिंग लाइन पर इकट्ठा होता है। और एक बार जब सभी तैयार हो जाते हैं, तो हर कोई एक ही समय में प्रारंभिक संकेत प्राप्त करता है और एक साथ शुरू होता है। उदाहरण:

public static void main(String[] args) {
	CountDownLatch countDownLatch = new CountDownLatch(3);
	Runnable task = () -> {
		try {
			countDownLatch.countDown();
			System.out.println("Countdown: " + countDownLatch.getCount());
			countDownLatch.await();
			System.out.println("Finished");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	for (int i = 0; i < 3; i++) {
		new Thread(task).start();
 	}
}
सबसे पहले, हम पहले कुंडी को बताते हैं countDown()। Google उलटी गिनती को "शून्य के विपरीत क्रम में अंकों की गिनती का एक कार्य" के रूप में परिभाषित करता है। और फिर हम कुंडी को कहते हैं await(), यानी तब तक प्रतीक्षा करें जब तक काउंटर शून्य न हो जाए। दिलचस्प बात यह है कि यह एक बार का काउंटर है। जावा प्रलेखन कहता है, "जब धागे को बार-बार इस तरह से गिनना चाहिए, तो इसके बजाय साइक्लिक बैरियर का उपयोग करें"। दूसरे शब्दों में, यदि आपको पुन: प्रयोज्य काउंटर की आवश्यकता है, तो आपको एक अलग विकल्प की आवश्यकता है: CyclicBarrier.

चक्रीय बैरियर

जैसा कि नाम से ही स्पष्ट है, CyclicBarrierएक "पुनः प्रयोग करने योग्य" बाधा है। हमें java.util.concurrent.CyclicBarrierकक्षा आयात करने की आवश्यकता होगी। आइए एक उदाहरण देखें:

public static void main(String[] args) throws InterruptedException {
	Runnable action = () -> System.out.println("On your mark!");
	CyclicBarrier barrier = new CyclicBarrier(3, action);
	Runnable task = () -> {
		try {
			barrier.await();
			System.out.println("Finished");
		} catch (BrokenBarrierException | InterruptedException e) {
			e.printStackTrace();
		}
	};
	System.out.println("Limit: " + barrier.getParties());
	for (int i = 0; i < 3; i++) {
		new Thread(task).start();
	}
}
जैसा कि आप देख सकते हैं, थ्रेड awaitविधि को चलाता है, अर्थात यह प्रतीक्षा करता है। इस मामले में, बाधा मूल्य घट जाती है। बैरियर को टूटा हुआ माना जाता है ( barrier.isBroken()) जब उलटी गिनती शून्य हो जाती है। बाधा को रीसेट करने के लिए, आपको उस reset()विधि को कॉल करने की आवश्यकता है जिसमें CountDownLatchनहीं है।

एक्सचेंजर

अगला तंत्र एक्सचेंजर है। इस संदर्भ में, एक एक्सचेंज एक तुल्यकालन बिंदु है जहां चीजों का आदान-प्रदान या अदला-बदली की जाती है। जैसा कि आप उम्मीद करेंगे, एक Exchangerऐसा वर्ग है जो एक्सचेंज या स्वैप करता है। आइए सबसे सरल उदाहरण देखें:

public static void main(String[] args) {
	Exchanger<String> exchanger = new Exchanger<>();
	Runnable task = () -> {
		try {
			Thread thread = Thread.currentThread();
			String withThreadName = exchanger.exchange(thread.getName());
			System.out.println(thread.getName() + " exchanged with " + withThreadName);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	new Thread(task).start();
	new Thread(task).start();
}
यहां हम दो धागे शुरू करते हैं। उनमें से प्रत्येक एक्सचेंज विधि चलाता है और दूसरे थ्रेड के लिए भी एक्सचेंज विधि चलाने की प्रतीक्षा करता है। ऐसा करने में, धागे पारित तर्कों का आदान-प्रदान करते हैं। दिलचस्प। क्या यह आपको कुछ याद नहीं दिलाता? यह की याद दिलाता है SynchronousQueue, जो के केंद्र में स्थित है CachedThreadPool। स्पष्टता के लिए, यहाँ एक उदाहरण है:

public static void main(String[] args) throws InterruptedException {
	SynchronousQueue<String> queue = new SynchronousQueue<>();
	Runnable task = () -> {
		try {
			System.out.println(queue.take());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	new Thread(task).start();
	queue.put("Message");
}
उदाहरण से पता चलता है कि जब कोई नया थ्रेड शुरू होता है, तो वह प्रतीक्षा करेगा, क्योंकि कतार खाली होगी। और फिर मुख्य धागा "संदेश" स्ट्रिंग को कतार में रखता है। क्या अधिक है, यह तब तक रुकेगा जब तक कि कतार से यह स्ट्रिंग प्राप्त नहीं हो जाती। आप इस विषय के बारे में और जानने के लिए " सिंक्रोनस क्यू बनाम एक्सचेंजर " भी पढ़ सकते हैं ।

फेजर

हमने सबसे अच्छे को आखिर के लिए सहेज कर रखा है — Phaser. हमें java.util.concurrent.Phaserकक्षा आयात करने की आवश्यकता होगी। आइए एक साधारण उदाहरण देखें:

public static void main(String[] args) throws InterruptedException {
        Phaser phaser = new Phaser();
        // By calling the register method, we register the current (main) thread as a party
        phaser.register();
        System.out.println("Phasecount is " + phaser.getPhase());
        testPhaser(phaser);
        testPhaser(phaser);
        testPhaser(phaser);
        // After 3 seconds, we arrive at the barrier and deregister. Number of arrivals = number of registrations = start
        Thread.sleep(3000);
        phaser.arriveAndDeregister();
        System.out.println("Phasecount is " + phaser.getPhase());
    }

    private static void testPhaser(final Phaser phaser) {
        // We indicate that there will be a +1 party on the Phaser
        phaser.register();
        // Start a new thread
        new Thread(() -> {
            String name = Thread.currentThread().getName();
            System.out.println(name + " arrived");
            phaser.arriveAndAwaitAdvance(); // The threads register arrival at the phaser.
            System.out.println(name + " after passing barrier");
        }).start();
    }
उदाहरण दिखाता है कि जब Phaserपंजीकरण की संख्या बैरियर पर आगमन की संख्या से मेल खाती है, तो बाधा टूट जाती है। आप इस GeeksforGeeks लेखPhaser को पढ़कर और अधिक परिचित हो सकते हैं ।

सारांश

जैसा कि आप इन उदाहरणों से देख सकते हैं, थ्रेड्स को सिंक्रोनाइज़ करने के कई तरीके हैं। इससे पहले, मैंने मल्टीथ्रेडिंग के पहलुओं को याद करने की कोशिश की थी। मुझे उम्मीद है कि इस श्रृंखला की पिछली किस्तें उपयोगी थीं। कुछ लोग कहते हैं कि मल्टीथ्रेडिंग का मार्ग "जावा कंसुरेंसी इन प्रैक्टिस" पुस्तक से शुरू होता है। हालांकि इसे 2006 में जारी किया गया था, लोगों का कहना है कि यह पुस्तक काफी मूलभूत है और आज भी प्रासंगिक है। उदाहरण के लिए, आप यहां चर्चा पढ़ सकते हैं: क्या "जावा कंसुरेंसी इन प्रैक्टिस" अभी भी मान्य है? . चर्चा में लिंक पढ़ना भी उपयोगी है। उदाहरण के लिए, The Well-Gounded Java Developer पुस्तक का एक लिंक है , और हम विशेष रूप से अध्याय 4. आधुनिक संगामिति का उल्लेख करेंगे । इस विषय के बारे में एक संपूर्ण समीक्षा भी है:क्या "जावा कंसुरेंसी इन प्रैक्टिस" अभी भी जावा 8 के युग में मान्य है? यह लेख इस विषय को सही मायने में समझने के लिए और क्या पढ़ना चाहिए, इसके बारे में सुझाव भी देता है। उसके बाद, आप ओसीए/ओसीपी जावा एसई 8 प्रोग्रामर प्रैक्टिस टेस्ट जैसी महान पुस्तक देख सकते हैं । हम दूसरे परिवर्णी शब्द में रुचि रखते हैं: OCP (ओरेकल सर्टिफाइड प्रोफेशनल)। आपको "अध्याय 20: Java Concurrency" में परीक्षण मिलेंगे। इस पुस्तक में स्पष्टीकरण के साथ प्रश्न और उत्तर दोनों हैं। उदाहरण के लिए: बेहतर एक साथ: जावा और थ्रेड क्लास।  भाग VI - आग बुझाओ!  - 3बहुत से लोग यह कहना शुरू कर सकते हैं कि यह प्रश्न विधियों को याद करने का एक और उदाहरण है। एक ओर, हाँ। दूसरी ओर, आप इस प्रश्न का उत्तर याद करके दे सकते हैं कि यह ExecutorServiceएक प्रकार का "अपग्रेड" है Executor। औरExecutorइसका उद्देश्य केवल थ्रेड्स बनाने के तरीके को छिपाना है, लेकिन यह उन्हें निष्पादित करने का मुख्य तरीका नहीं है, अर्थात Runnableएक नए थ्रेड पर ऑब्जेक्ट प्रारंभ करें। इसलिए कोई नहीं है execute(Callable)- क्योंकि में ExecutorService, Executorबस उन विधियों को जोड़ता है submit()जो किसी Futureवस्तु को वापस कर सकते हैं। बेशक, हम तरीकों की एक सूची याद कर सकते हैं, लेकिन कक्षाओं की प्रकृति के बारे में हमारे ज्ञान के आधार पर अपना उत्तर देना बहुत आसान है। और यहाँ विषय पर कुछ अतिरिक्त सामग्री दी गई है: बेहतर एक साथ: जावा और थ्रेड क्लास। भाग I - थ्रेड्स ऑफ़ एक्जीक्यूशन बेहतर एक साथ: जावा और थ्रेड क्लास। भाग II - तुल्यकालन बेहतर एक साथ: जावा और थ्रेड वर्ग। भाग III - इंटरेक्शन बेटर टुगेदर: जावा और थ्रेड क्लास। भाग IV - कॉल करने योग्य, भविष्य और मित्र बेहतर एक साथ: जावा और थ्रेड वर्ग। भाग वी - एक्ज़ीक्यूटर, थ्रेडपूल, फोर्क / जॉइन
टिप्पणियां
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION