जवाबों:
wait()
और notify()
तरीकों जब तक एक विशिष्ट स्थिति उत्पन्न होने पर ब्लॉक करने के लिए एक धागा अनुमति देने के लिए एक तंत्र प्रदान करने के लिए डिजाइन किए हैं। इसके लिए मुझे लगता है कि आप एक अवरुद्ध कतार कार्यान्वयन लिखना चाहते हैं, जहाँ आपके पास कुछ निश्चित आकार समर्थन तत्वों की दुकान है।
पहली चीज जो आपको करनी है वह यह है कि उन स्थितियों की पहचान करें जिन्हें आप प्रतीक्षा करने के तरीके चाहते हैं। इस स्थिति में, आप चाहते हैं कि put()
जब तक स्टोर में खाली जगह take()
न हो, तब तक ब्लॉक करने की विधि होगी, और आप तब तक ब्लॉक करने की विधि चाहते हैं जब तक कि लौटने के लिए कुछ तत्व न हो।
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public synchronized void put(T element) throws InterruptedException {
while(queue.size() == capacity) {
wait();
}
queue.add(element);
notify(); // notifyAll() for multiple producer/consumer threads
}
public synchronized T take() throws InterruptedException {
while(queue.isEmpty()) {
wait();
}
T item = queue.remove();
notify(); // notifyAll() for multiple producer/consumer threads
return item;
}
}
जिस तरह से आपको प्रतीक्षा और सूचना तंत्र का उपयोग करना चाहिए, उस पर ध्यान देने के लिए कुछ चीजें हैं।
सबसे पहले, आप यह सुनिश्चित करने की जरूरत है कि किसी भी कॉल करने के लिए wait()
या notify()
कोड का एक सिंक्रनाइज़ क्षेत्र के भीतर (के साथ कर रहे हैं wait()
और notify()
एक ही वस्तु पर समन्वयित हो रहा कॉल)। इसका कारण (मानक थ्रेड सुरक्षा चिंताओं के अलावा) कुछ एक चूक संकेत के रूप में जाना जाता है।
इसका एक उदाहरण यह है कि एक धागा कॉल हो सकता है put()
जब कतार पूरी होती है, तो यह स्थिति की जांच करता है, देखता है कि कतार पूरी है, हालांकि इससे पहले कि यह एक और धागा निर्धारित हो सके। यह दूसरा धागा तब take()
कतार से एक तत्व है, और प्रतीक्षा के धागे को सूचित करता है कि कतार अब पूरी नहीं है। क्योंकि पहले थ्रेड ने पहले ही स्थिति की जांच कर ली है, यह wait()
फिर से शेड्यूल होने के बाद कॉल करेगा , हालांकि यह प्रगति कर सकता है।
एक साझा ऑब्जेक्ट पर सिंक्रनाइज़ करके, आप यह सुनिश्चित कर सकते हैं कि यह समस्या उत्पन्न नहीं होती है, क्योंकि दूसरे थ्रेड की take()
कॉल तब तक प्रगति नहीं कर पाएगी जब तक कि पहले थ्रेड वास्तव में अवरुद्ध न हो।
दूसरी बात, आपको स्पॉर्टी वेक-अप के रूप में ज्ञात समस्या के कारण, यदि आप एक स्टेटमेंट के बजाय, कुछ समय लूप में चेक कर रहे हैं, तो आपको एक स्टेटमेंट डालना होगा। यह वह जगह है जहां एक प्रतीक्षा धागा को कभी-कभी बिना notify()
बुलाए सक्रिय किया जा सकता है। इस चेक को थोड़ी देर में लगाने से यह सुनिश्चित हो जाएगा कि यदि एक स्पुरियस वेक-अप होता है, तो स्थिति को फिर से जाँच लिया जाएगा, और थ्रेड wait()
फिर से कॉल करेगा ।
जैसा कि कुछ अन्य उत्तरों ने उल्लेख किया है, जावा 1.5 ने एक नया संगाम पुस्तकालय ( java.util.concurrent
पैकेज में) पेश किया, जिसे प्रतीक्षा / सूचना तंत्र पर उच्च स्तर का अमूर्त प्रदान करने के लिए डिज़ाइन किया गया था। इन नई विशेषताओं का उपयोग करके, आप मूल उदाहरण को फिर से लिख सकते हैं जैसे:
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public void put(T element) throws InterruptedException {
lock.lock();
try {
while(queue.size() == capacity) {
notFull.await();
}
queue.add(element);
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while(queue.isEmpty()) {
notEmpty.await();
}
T item = queue.remove();
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}
बेशक अगर आप वास्तव में एक अवरुद्ध कतार जरूरत है, तो आप के एक कार्यान्वयन का उपयोग करना चाहिए BlockingQueue इंटरफ़ेस।
इसके अलावा, इस तरह के सामान के लिए, मैं प्रैक्टिस में जावा कॉनएरेबिलिटी की अत्यधिक अनुशंसा करता हूं , क्योंकि यह सब कुछ शामिल करता है जिसे आप समसामयिक संबंधित समस्याओं और समाधानों के बारे में जानना चाहते हैं।
एक कतार का उदाहरण नहीं, लेकिन बेहद सरल :)
class MyHouse {
private boolean pizzaArrived = false;
public void eatPizza(){
synchronized(this){
while(!pizzaArrived){
wait();
}
}
System.out.println("yumyum..");
}
public void pizzaGuy(){
synchronized(this){
this.pizzaArrived = true;
notifyAll();
}
}
}
कुछ महत्वपूर्ण बिंदु:
1) कभी नहीं
if(!pizzaArrived){
wait();
}
हमेशा (स्थिति) का उपयोग करें, क्योंकि
while(!pizzaExists){ wait(); }
।2) आपको प्रतीक्षा / नौसिखिया को लागू करने से पहले ताला (सिंक्रनाइज़) रखना होगा। जागने से पहले थ्रेड्स को लॉक भी हासिल करना होता है।
3) अपने सिंक्रनाइज़ किए गए ब्लॉक के भीतर किसी भी लॉक को प्राप्त करने से बचने की कोशिश करें और विदेशी तरीकों को लागू न करने का प्रयास करें (वे तरीके जो आपको नहीं पता कि वे क्या कर रहे हैं)। यदि आपको करना है, तो गतिरोध से बचने के उपाय करना सुनिश्चित करें।
4) नोटिफ़िकेशन () से सावधान रहें। जब तक आप जानते हैं कि आप क्या कर रहे हैं, तब तक InformAll () के साथ रहें।
5) अंतिम, लेकिन कम से कम, अभ्यास में जावा कॉनएरेबिलिटी नहीं पढ़ें !
pizzaArrived
झंडे का उपयोग क्यों ? अगर झंडे को कॉल के बिना बदला जाता है notify
तो इसका कोई प्रभाव नहीं होगा। इसके अलावा सिर्फ उदाहरण के साथ wait
और notify
काम करता है।
synchronized
कीवर्ड द्वारा संरक्षित किया जाता है , तो यह चर घोषित करने के लिए अतिरेक है volatile
, और भ्रम से बचने के लिए इसे टालने की सिफारिश की जाती है @mrida
भले ही आपने wait()
और notify()
विशेष रूप से पूछा , मुझे लगता है कि यह उद्धरण अभी भी महत्वपूर्ण है:
जोश बलोच, प्रभावी जावा 2 संस्करण , आइटम 69: संगामिति उपयोगिताओं को प्राथमिकता दें wait
और notify
(उनके जोर दें):
उपयोग करने
wait
औरnotify
सही ढंग से करने की कठिनाई को देखते हुए , आपको इसके बजाय उच्च-स्तरीय संगामिति उपयोगिताओं का उपयोग करना चाहिए [...] का उपयोग करनाwait
औरnotify
सीधे "संगामिति विधानसभा भाषा" में प्रोग्रामिंग की तरह है, जैसा कि प्रदान की गई उच्च-स्तरीय भाषा की तुलना में हैjava.util.concurrent
। शायद ही कभी, उपयोग करने का कारणwait
औरnotify
नए कोड में होता है ।
notify()
और wait()
फिर से उपयोग करने की आवश्यकता नहीं होगी
क्या आपने इस जावा ट्यूटोरियल को देखा है ?
इसके अलावा, मैं आपको वास्तविक सॉफ़्टवेयर में इस तरह के सामान के साथ खेलने से दूर रहने की सलाह दूंगा। इसके साथ खेलना अच्छा है, इसलिए आपको पता है कि यह क्या है, लेकिन समरूपता में सभी जगह नुकसान होते हैं। यदि आप अन्य लोगों के लिए सॉफ़्टवेयर का निर्माण कर रहे हैं तो उच्च स्तर के अमूर्त और सिंक्रनाइज़ किए गए संग्रह या JMS कतारों का उपयोग करना बेहतर है।
कम से कम मैं जो करता हूं। मैं कंसीडर विशेषज्ञ नहीं हूं, इसलिए जहां भी संभव हो मैं हाथ से धागे को संभालने से दूर रहता हूं।
उदाहरण
public class myThread extends Thread{
@override
public void run(){
while(true){
threadCondWait();// Circle waiting...
//bla bla bla bla
}
}
public synchronized void threadCondWait(){
while(myCondition){
wait();//Comminucate with notify()
}
}
}
public class myAnotherThread extends Thread{
@override
public void run(){
//Bla Bla bla
notify();//Trigger wait() Next Step
}
}
थ्रेडिंग में प्रतीक्षा () और अनौपचारिक () के लिए उदाहरण।
एक सिंक्रनाइज़ेशन स्थिर सरणी सूची का उपयोग संसाधन और प्रतीक्षा के रूप में किया जाता है () विधि को कहा जाता है यदि सरणी सूची खाली है। एक बार एरे लिस्ट में एलीमेंट जुड़ने के बाद नोटिफ़िकेशन () विधि लागू होती है।
public class PrinterResource extends Thread{
//resource
public static List<String> arrayList = new ArrayList<String>();
public void addElement(String a){
//System.out.println("Add element method "+this.getName());
synchronized (arrayList) {
arrayList.add(a);
arrayList.notifyAll();
}
}
public void removeElement(){
//System.out.println("Remove element method "+this.getName());
synchronized (arrayList) {
if(arrayList.size() == 0){
try {
arrayList.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
arrayList.remove(0);
}
}
}
public void run(){
System.out.println("Thread name -- "+this.getName());
if(!this.getName().equalsIgnoreCase("p4")){
this.removeElement();
}
this.addElement("threads");
}
public static void main(String[] args) {
PrinterResource p1 = new PrinterResource();
p1.setName("p1");
p1.start();
PrinterResource p2 = new PrinterResource();
p2.setName("p2");
p2.start();
PrinterResource p3 = new PrinterResource();
p3.setName("p3");
p3.start();
PrinterResource p4 = new PrinterResource();
p4.setName("p4");
p4.start();
try{
p1.join();
p2.join();
p3.join();
p4.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Final size of arraylist "+arrayList.size());
}
}
if(arrayList.size() == 0)
, मुझे लगता है कि यहाँ गलती हो सकती है।
notify
केवल एक धागा जगाता है। यदि दो उपभोक्ता धागे एक तत्व को निकालने के लिए प्रतिस्पर्धा कर रहे हैं, तो एक सूचना दूसरे उपभोक्ता धागे को जगा सकती है, जो इसके बारे में कुछ नहीं कर सकता है और वापस सो जाएगा (निर्माता के बजाय, जो हम उम्मीद कर रहे थे कि एक नया तत्व सम्मिलित होगा।) निर्माता धागा जगा नहीं है, कुछ भी नहीं डाला जाता है और अब तीनों धागे अनिश्चित काल तक सोते रहेंगे। मैंने अपनी पिछली टिप्पणी को हटा दिया क्योंकि यह कहा गया था कि (गलत तरीके से) वह