जावा थ्रेड को किसी अन्य थ्रेड के आउटपुट की प्रतीक्षा कैसे करें?


128

मैं एक एप्लिकेशन-लॉजिक-थ्रेड और एक डेटाबेस-एक्सेस-थ्रेड के साथ एक जावा एप्लिकेशन बना रहा हूं। वे दोनों पूरे जीवनकाल आवेदन के लिए बने रहते हैं और दोनों को उसी समय चलने की आवश्यकता होती है (सर्वर से बात करता है, उपयोगकर्ता से बात करता है; जब ऐप पूरी तरह से शुरू हो जाता है, तो मुझे काम करने के लिए दोनों की आवश्यकता होती है )।

हालांकि, स्टार्टअप पर, मुझे यह सुनिश्चित करने की ज़रूरत है कि शुरू में ऐप थ्रेड तब तक इंतजार करता है जब तक कि डीबी थ्रेड तैयार नहीं हो जाता है (वर्तमान में एक कस्टम पद्धति को मतदान करके निर्धारित किया जाता है dbthread.isReady())। अगर db धागा तैयार नहीं होता है तो मुझे ऐप थ्रेड ब्लॉक करने में कोई दिक्कत नहीं होगी।

Thread.join() समाधान की तरह नहीं दिखता है - DB थ्रेड केवल ऐप शटडाउन से बाहर निकलता है।

while (!dbthread.isReady()) {} काम करता है, लेकिन खाली लूप में बहुत अधिक प्रोसेसर चक्र होता है।

कोई अन्य विचार? धन्यवाद।

जवाबों:


128

मैं वास्तव में सुझाव दूंगा कि आप मल्टीथ्रेडिंग की जादुई दुनिया में शुरू करने से पहले सन के जावा कॉनएरेबिलिटी जैसे ट्यूटोरियल से गुजरें।

वहाँ भी कई अच्छी किताबें हैं ("जावा में समवर्ती प्रोग्रामिंग" के लिए Google, "अभ्यास में जावा कॉनएरेबिलिटी")।

अपने उत्तर पाने के लिए:

आपके कोड में जिसका इंतजार करना होगा dbThread, आपके पास कुछ इस तरह होना चाहिए:

//do some work
synchronized(objectYouNeedToLockOn){
    while (!dbThread.isReady()){
        objectYouNeedToLockOn.wait();
    }
}
//continue with work after dbThread is ready

अपने dbThreadतरीके से, आपको कुछ इस तरह करने की आवश्यकता होगी:

//do db work
synchronized(objectYouNeedToLockOn){
    //set ready flag to true (so isReady returns true)
    ready = true;
    objectYouNeedToLockOn.notifyAll();
}
//end thread run method here

objectYouNeedToLockOnमैं इन उदाहरणों में उपयोग कर रहा हूँ अधिमानतः उद्देश्य यह है कि आप प्रत्येक धागे से समवर्ती में हेरफेर करने की जरूरत है, या आप एक अलग बना सकता है Object(मैं तरीकों खुद को सिंक्रनाइज़ बनाने की सिफारिश नहीं होगा) उस उद्देश्य के लिए:

private final Object lock = new Object();
//now use lock in your synchronized blocks

अपनी समझ को आगे बढ़ाने के लिए:
उपरोक्त CountdownLatches5 करने के लिए अन्य (कभी-कभी बेहतर) तरीके हैं, जैसे जावा 5 के बाद से java.util.concurrentपैकेज और उप-संकुल में बहुत सारे निफ़्टी संगामिति वर्ग हैं। कंसीडर को जानने के लिए या अच्छी किताब पाने के लिए आपको वास्तव में ऑनलाइन सामग्री ढूंढनी होगी।


न ही सभी थ्रेड कोड को अच्छी तरह से वस्तुओं में एकीकृत किया जा सकता है, अगर मैं गलत नहीं हूं। इसलिए मुझे नहीं लगता कि ऑब्जेक्ट सिंक्रनाइज़ेशन का उपयोग इस थ्रेड से संबंधित कार्य को लागू करने का एक अच्छा तरीका है।
user1914692

@ user1914692: उपरोक्त दृष्टिकोण का उपयोग करने में क्या नुकसान नहीं हैं - सुनिश्चित करें कि आगे की व्याख्या करने की परवाह है?
पिस्कोर ने 13

1
@Piskvor: क्षमा करें मैंने यह बहुत पहले लिखा था और मैं लगभग भूल गया था कि मेरे दिमाग में क्या था। शायद मैं सिर्फ वस्तु तुल्यकालन के बजाय लॉक का उपयोग करने के लिए बेहतर था, बाद वाला पूर्व का एक सरलीकृत रूप था।
user1914692

मुझे समझ नहीं आता कि यह कैसे काम करता है। यदि थ्रेड aऑब्जेक्ट पर प्रतीक्षा कर रहा है तो कॉल करने के लिए synchronised(object)दूसरा थ्रेड कैसे जा सकता synchronized(object)है object.notifyAll()? मेरे कार्यक्रम में, सब कुछ बस synchronozedब्लॉकों पर अटक गया ।
टॉम ज़ातो -

@ TomášZato पहला धागा कॉल object.wait()को उस वस्तु पर प्रभावी ढंग से लॉक को अनलॉक करता है। जब दूसरा धागा अपने सिंक्रनाइज़ किए गए ब्लॉक को "बाहर" कर देता है, तो अन्य ऑब्जेक्ट को waitविधि से जारी किया जाता है और उस बिंदु पर लॉक को फिर से प्राप्त करता है।
रोजरपैक

140

1 के काउंटर के साथ एक CountDownLatch का उपयोग करें ।

CountDownLatch latch = new CountDownLatch(1);

अब ऐप थ्रेड में करें-

latch.await();

DB थ्रेड में, आपके द्वारा किए जाने के बाद, करें -

latch.countDown();

4
मुझे वास्तव में इसका समाधान सरलता पसंद है, हालाँकि पहली नजर में कोड के अर्थ को समझ पाना कठिन हो सकता है।
घातक-गिटार

3
इस उपयोग की आवश्यकता होती है जब आप उपयोग करने के लिए कुंडी का रीमेक बनाते हैं। Windows में एक Waitable घटना के लिए इसी तरह के उपयोग के लिए, आप एक BooleanLatch, या एक रीसेट CountDownLatch प्रयास करना चाहिए: docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/... stackoverflow.com/questions / 6595835 /…
फिएट

1
नमस्ते, अगर मैं पहली बार एक async विधि को कॉल करता हूं कि यह एक घटना को इस तरह से आग लगाने वाला है: 1) asyncFunc (); 2) latch.await (); फिर मैं एक बार इसे प्राप्त करने के बाद ईवेंट हैंडलिंग फ़ंक्शन में उलटी गिनती करता हूं। मैं यह कैसे सुनिश्चित कर सकता हूं कि घटना को पहले से नहीं संभाला जाएगा। lit.await () कहा जाता है? मैं लाइन 1 और 2 के बीच प्रीमेशन को रोकना चाहूंगा। धन्यवाद।
NioX5199

1
त्रुटियों के मामले में हमेशा के लिए प्रतीक्षा करने से बचने के लिए, countDown()एक finally{}ब्लॉक में
डैनियल एल्डर

23

आवश्यकता ::

  1. पिछले समाप्त होने तक अगले धागे के निष्पादन की प्रतीक्षा करने के लिए।
  2. अगला धागा तब तक शुरू नहीं होना चाहिए जब तक कि पिछला धागा बंद न हो जाए, समय की खपत के बावजूद।
  3. यह सरल और प्रयोग करने में आसान होना चाहिए।

उत्तर ::

@See java.util.concurrent.Future.get () डॉक्टर।

future.get () प्रतीक्षा करता है यदि गणना पूरा करने के लिए आवश्यक है, और फिर इसका परिणाम निकालता है।

काम हो गया!! नीचे उदाहरण देखें

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.junit.Test;

public class ThreadTest {

    public void print(String m) {
        System.out.println(m);
    }

    public class One implements Callable<Integer> {

        public Integer call() throws Exception {
            print("One...");
            Thread.sleep(6000);
            print("One!!");
            return 100;
        }
    }

    public class Two implements Callable<String> {

        public String call() throws Exception {
            print("Two...");
            Thread.sleep(1000);
            print("Two!!");
            return "Done";
        }
    }

    public class Three implements Callable<Boolean> {

        public Boolean call() throws Exception {
            print("Three...");
            Thread.sleep(2000);
            print("Three!!");
            return true;
        }
    }

    /**
     * @See java.util.concurrent.Future.get() doc
     *      <p>
     *      Waits if necessary for the computation to complete, and then
     *      retrieves its result.
     */
    @Test
    public void poolRun() throws InterruptedException, ExecutionException {
        int n = 3;
        // Build a fixed number of thread pool
        ExecutorService pool = Executors.newFixedThreadPool(n);
        // Wait until One finishes it's task.
        pool.submit(new One()).get();
        // Wait until Two finishes it's task.
        pool.submit(new Two()).get();
        // Wait until Three finishes it's task.
        pool.submit(new Three()).get();
        pool.shutdown();
    }
}

इस कार्यक्रम का आउटपुट ::

One...
One!!
Two...
Two!!
Three...
Three!!

आप देख सकते हैं कि अपने कार्य को पूरा करने से पहले 6sec लगता है जो अन्य धागे से अधिक है। इसलिए Future.get () कार्य पूरा होने तक प्रतीक्षा करता है।

यदि आप Future.get का उपयोग नहीं करते हैं () यह समय समाप्त होने का इंतजार नहीं करता है और समय की खपत को निष्पादित करता है।

जावा संगति के साथ गुड लक।


आपके उत्तर के लिए धन्यवाद! मैंने CountdownLatches का उपयोग किया है , लेकिन तुम्हारा बहुत अधिक लचीला दृष्टिकोण है।
पिस्कोवर ने 14:18 बजे बिल्डिंग

9

बहुत सारे सही उत्तर लेकिन सरल उदाहरण के बिना .. यहाँ एक आसान और सरल तरीका है कि कैसे उपयोग करें CountDownLatch:

//inside your currentThread.. lets call it Thread_Main
//1
final CountDownLatch latch = new CountDownLatch(1);

//2
// launch thread#2
new Thread(new Runnable() {
    @Override
    public void run() {
        //4
        //do your logic here in thread#2

        //then release the lock
        //5
        latch.countDown();
    }
}).start();

try {
    //3 this method will block the thread of latch untill its released later from thread#2
    latch.await();
} catch (InterruptedException e) {
    e.printStackTrace();
}

//6
// You reach here after  latch.countDown() is called from thread#2

8
public class ThreadEvent {

    private final Object lock = new Object();

    public void signal() {
        synchronized (lock) {
            lock.notify();
        }
    }

    public void await() throws InterruptedException {
        synchronized (lock) {
            lock.wait();
        }
    }
}

इस वर्ग का उपयोग तब करें:

एक सूत्र बनाएँ:

ThreadEvent resultsReady = new ThreadEvent();

इस विधि में परिणाम की प्रतीक्षा है:

resultsReady.await();

और उस विधि में जो सभी परिणाम बनाने के बाद परिणाम बना रहा है:

resultsReady.signal();

संपादित करें:

(इस पोस्ट को संपादित करने के लिए क्षमा करें, लेकिन इस कोड की दौड़ की स्थिति बहुत खराब है और मेरे पास टिप्पणी करने के लिए पर्याप्त प्रतिष्ठा नहीं है)

आप इसका उपयोग केवल तभी कर सकते हैं जब आप 100% सुनिश्चित हों कि संकेत () प्रतीक्षा के बाद कहा जाता है ()। यह एक बड़ा कारण है कि आप उदाहरण के लिए विंडोज इवेंट जैसे जावा ऑब्जेक्ट का उपयोग क्यों नहीं कर सकते हैं।

यदि कोड इस क्रम में चलता है:

Thread 1: resultsReady.signal();
Thread 2: resultsReady.await();

फिर धागा 2 हमेशा के लिए इंतजार करेगा । ऐसा इसलिए है क्योंकि Object.notify () केवल वर्तमान में चल रहे थ्रेड्स में से एक को जगाता है। बाद में प्रतीक्षा करने वाला एक धागा जागृत नहीं है। यह इस बात से बहुत अलग है कि मैं घटनाओं के काम करने की उम्मीद कैसे करता हूं, जहां एक घटना का संकेत तब तक दिया जाता है जब तक कि ए) या बी के लिए प्रतीक्षा नहीं की जाती है।

नोट: ज्यादातर समय, आपको InformAll () का उपयोग करना चाहिए, लेकिन यह ऊपर "हमेशा के लिए प्रतीक्षा करें" समस्या के लिए प्रासंगिक नहीं है।


7

काउंटडाउन लॉच क्लास को java.util.concurrentपैकेज से बाहर करने की कोशिश करें , जो उच्च स्तर के सिंक्रोनाइज़ेशन तंत्र प्रदान करता है, जो कि किसी भी निम्न स्तर के सामान की तुलना में कम त्रुटि वाला होता है।


6

आप इसे दो थ्रेड्स के बीच साझा की गई एक एक्सचेंजर ऑब्जेक्ट का उपयोग कर सकते हैं :

private Exchanger<String> myDataExchanger = new Exchanger<String>();

// Wait for thread's output
String data;
try {
  data = myDataExchanger.exchange("");
} catch (InterruptedException e1) {
  // Handle Exceptions
}

और दूसरे धागे में:

try {
    myDataExchanger.exchange(data)
} catch (InterruptedException e) {

}

जैसा कि दूसरों ने कहा है, इस हल्के-फुल्के और सिर्फ कॉपी-पेस्ट कोड को न लें। पहले कुछ पढ़ो।


4

भविष्य से इंटरफ़ेस java.lang.concurrentपैकेज एक और धागा में गणना के परिणाम के लिए पहुँच प्रदान करने के लिए बनाया गया है।

पर एक नजर डालें FutureTask और ExecutorService बात इस तरह का करने का एक रेडीमेड रास्ता के लिए।

मैं दृढ़ता से और बहु-सूत्रण में रुचि रखने वाले किसी भी व्यक्ति में जावा कॉनएरेबिलिटी को अभ्यास में पढ़ने की सलाह दूंगा। यह स्पष्ट रूप से जावा पर ध्यान केंद्रित करता है, लेकिन अन्य भाषाओं में भी काम करने वाले लोगों के लिए बहुत अधिक मांस है।


2

यदि आप कुछ जल्दी और गंदे चाहते हैं, तो आप अपने लूप में थ्रेड.स्लीप () कॉल को जोड़ सकते हैं। यदि डेटाबेस लाइब्रेरी ऐसी चीज़ है जिसे आप बदल नहीं सकते हैं, तो वास्तव में कोई अन्य आसान समाधान नहीं है। एक प्रतीक्षा अवधि के साथ तैयार होने तक डेटाबेस को मतदान करना प्रदर्शन को नहीं मारेगा।

while (!dbthread.isReady()) {
  Thread.sleep(250);
}

शायद ही कुछ ऐसा हो जिसे आप सुरुचिपूर्ण कोड कह सकते हैं, लेकिन काम पूरा हो जाता है।

यदि आप डेटाबेस कोड को संशोधित कर सकते हैं, तो अन्य उत्तरों में प्रस्तावित म्यूटेक्स का उपयोग करना बेहतर है।


3
यह बहुत ज्यादा इंतजार में व्यस्त है। जावा 5 के यूज़ से कंस्ट्रक्शंस का इस्तेमाल करना। कॉन्ट्रैक्ट पैकेज को जाने का रास्ता होना चाहिए। stackoverflow.com/questions/289434/… मुझे अब के लिए सबसे अच्छा समाधान के रूप में दिखता है।
Cem Catikkas

यह प्रतीक्षा में व्यस्त है, लेकिन अगर यह केवल इस विशेष स्थान पर आवश्यक है, और अगर डीबी लाइब्रेरी तक कोई पहुंच नहीं है, तो आप और क्या कर सकते हैं? व्यस्त इंतजार जरूरी बुराई नहीं है
मारियो ओर्टेगन

2

यह सभी भाषाओं पर लागू होता है:

आप एक ईवेंट / श्रोता मॉडल रखना चाहते हैं। आप किसी विशेष कार्यक्रम की प्रतीक्षा करने के लिए एक श्रोता बनाते हैं। ईवेंट आपके वर्कर थ्रेड में बनाया जाएगा (या सिग्नल किया गया)। यह थ्रेड को तब तक ब्लॉक करेगा, जब तक कि कोई शर्त पूरी न हो जाए, यह देखने के लिए लगातार मतदान के बजाय सिग्नल प्राप्त होता है, जैसे कि वर्तमान में आपके पास जो समाधान है।

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

मेरा सुझाव है कि आप अपने मामले को लागू करने से पहले इस प्रतिमान को बेहतर ढंग से समझने के लिए घटनाओं और घटना संचालकों का उपयोग करने की अवधारणाओं पर ध्यान दें।

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

Thread-A Locks lock-a
Run thread-B
Thread-B waits for lock-a
Thread-A unlocks lock-a (causing Thread-B to continue)
Thread-A waits for lock-b 
Thread-B completes and unlocks lock-b

2

आप एक धागे में अवरुद्ध कतार से पढ़ सकते हैं और इसे दूसरे धागे में लिख सकते हैं।


1

जबसे

  1. join() खारिज कर दिया गया है
  2. आप पहले से ही CountDownLatch और का उपयोग कर रहे हैं
  3. Future.get () पहले से ही अन्य विशेषज्ञों द्वारा प्रस्तावित है,

आप अन्य विकल्पों पर विचार कर सकते हैं:

  1. invokeAll सेExecutorService

    invokeAll(Collection<? extends Callable<T>> tasks)

    दिए गए कार्यों को निष्पादित करता है, सभी पूर्ण होने पर अपनी स्थिति और परिणामों को पकड़े हुए फ्यूचर्स की सूची लौटाते हैं।

  2. ForkJoinPool या newWorkStealingPool से Executors(के बाद से जावा 8 विज्ञप्ति)

    सभी उपलब्ध प्रोसेसरों को अपने लक्ष्य समांतरता स्तर के रूप में उपयोग करते हुए एक कार्य-चोरी धागा पूल बनाता है।


-1

यहाँ छवि विवरण दर्ज करें

यह विचार लागू कर सकते हैं ?. यदि आप CountdownLatches या Semaphores का उपयोग करता है सही काम करता है, लेकिन अगर आप एक साक्षात्कार के लिए सबसे आसान जवाब की तलाश में हैं तो मुझे लगता है कि यह लागू हो सकता है।


1
यह साक्षात्कार के लिए कैसे ठीक है लेकिन वास्तविक कोड के लिए नहीं?
पिस्कोर ने

क्योंकि इस मामले में क्रमिक रूप से एक दूसरे की प्रतीक्षा कर रहा है। सबसे अच्छा समाधान सेमाफोर का उपयोग किया जा सकता है क्योंकि काउंटडाउनचैट्स का उपयोग करना, जो कि यहां दिया गया सबसे अच्छा जवाब है, थ्रेड कभी सोने नहीं जाता है जिसका अर्थ है कि सीपीयू चक्र का उपयोग।
फ्रेंको

लेकिन बिंदु "उन्हें क्रमिक रूप से नहीं चला रहा था।" मैं इस प्रश्न को स्पष्ट करने के लिए संपादन करूँगा: GUI थ्रेड डेटाबेस तैयार होने तक प्रतीक्षा करता है, और फिर दोनों ऐप के बाकी निष्पादन के लिए एक साथ चलते हैं : GUI थ्रेड DB थ्रेड को कमांड भेजता है और परिणाम पढ़ता है। (और फिर से: कोड का क्या मतलब है जो एक साक्षात्कार में इस्तेमाल किया जा सकता है, लेकिन वास्तविक कोड में नहीं; अधिकांश तकनीकी साक्षात्कारकर्ता जो मुझे मिले हैं, कोड में पृष्ठभूमि थी और वही प्रश्न पूछेंगे; मुझे एक वास्तविक ऐप के लिए इस चीज़ की आवश्यकता थी; मैं उस समय लिख रहा था, होमवर्क के ज़रिए नहीं।)
पिस्कॉर ने

1
इसलिए। यह एक उत्पादक उपभोक्ता समस्या है जिसका उपयोग सेमाफोरेस किया जाता है। मैं एक उदाहरण करने की कोशिश करूंगा
फ्रेंको

मैंने एक प्रोजेक्ट बनाया है github.com/francoj22/SemProducerConsumer/blob/master/src/com/… । यह बढ़िया काम करता है।
फ्रेंको
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.