जब तक सभी धागे जावा में अपना काम खत्म नहीं करते तब तक प्रतीक्षा करें


91

मैं एक आवेदन लिख रहा हूं जिसमें 5 धागे हैं जो एक साथ वेब से कुछ जानकारी प्राप्त करते हैं और एक बफर क्लास में 5 अलग-अलग फ़ील्ड भरते हैं।
मुझे बफ़र डेटा को मान्य करने और डेटाबेस में संग्रहीत करने की आवश्यकता है जब सभी थ्रेड्स ने अपना काम समाप्त कर लिया।
मैं यह कैसे कर सकता हूं (जब सभी थ्रेड्स ने अपना काम पूरा कर लिया है) सतर्क हो जाएं?


4
थ्रेड.जॉइन समस्या को हल करने के लिए एक बहुत ही निम्न स्तर का बहुत ही जावा आइडियोसिंक्रेटिक तरीका है। इसके अलावा यह समस्याग्रस्त है क्योंकि थ्रेड एपीआई त्रुटिपूर्ण है: आप यह नहीं जान सकते कि जॉइन पूर्ण हुआ या नहीं ( जावा कॉनएरेबिलिटी इन प्रैक्टिस देखें )। काउंटडाउन लैंच का उपयोग करने की तरह उच्च स्तर का अमूर्त, बेहतर हो सकता है और उन प्रोग्रामर को अधिक स्वाभाविक लगेगा जो जावा-आइडियोसिंचैटिक मानसिकता में "अटक" नहीं रहे हैं। मेरे साथ बहस मत करो, डग ले के साथ बहस करो; )
सेड्रिक मार्टिन

जवाबों:


119

मैं जो दृष्टिकोण लेता हूं, वह थ्रेड्स के पूल को प्रबंधित करने के लिए एक एक्सक्यूसोर सर्विस का उपयोग करना है ।

ExecutorService es = Executors.newCachedThreadPool();
for(int i=0;i<5;i++)
    es.execute(new Runnable() { /*  your task */ });
es.shutdown();
boolean finished = es.awaitTermination(1, TimeUnit.MINUTES);
// all tasks have finished or the time has been reached.

7
@ लियोनिड जो वास्तव में शटडाउन () करता है।
पीटर लॉरी

3
while(!es.awaitTermination(1, TimeUnit.MINUTES));
कुंभ राशि

3
@AquariusPower आप इसे लंबे समय तक, या हमेशा के लिए प्रतीक्षा करने के लिए कह सकते हैं।
पीटर लॉरी

1
अच्छा मैं समझा; इसलिए मैंने लूप में एक संदेश जोड़ते हुए कहा कि यह सभी धागे खत्म होने की प्रतीक्षा कर रहा है; धन्यवाद!
कुंभ राशि

1
@PeterLawrey, क्या कॉल करना आवश्यक है es.shutdown();? क्या हुआ अगर मैं एक कोड है, जिसमें मैं एक धागा का उपयोग कर मार डाला बारे es.execute(runnableObj_ZipMaking);में tryब्लॉक और में finallyमैं कहा जाता है boolean finshed = es.awaitTermination(10, TimeUnit.MINUTES);। इसलिए मुझे लगता है कि यह तब तक इंतजार करना चाहिए जब तक कि सभी धागे अपना काम पूरा नहीं करते हैं या टाइमआउट होता है (जो भी पहले हो), क्या मेरी धारणा सही है? या कॉल shutdown()अनिवार्य है?
अमोघ

52

आप joinथ्रेड्स को कर सकते हैं । थ्रेड पूर्ण होने तक ब्लॉक में शामिल हों।

for (Thread thread : threads) {
    thread.join();
}

ध्यान दें कि joinफेंकता है a InterruptedException। आपको यह तय करना होगा कि ऐसा होने पर क्या करना है (जैसे अनावश्यक काम को रोकने के लिए अन्य थ्रेड को रद्द करने का प्रयास करें)।


1
क्या ये धागे एक दूसरे के समानांतर या क्रमिक रूप से चलते हैं?
जेम्स वेबस्टर

5
@ जेम्सवेस्टर: समानांतर।
आरवाईएन

4
@ जेम्स वेबस्टर: कथन का t.join();अर्थ है कि थ्रेड समाप्त होने तक वर्तमान थ्रेड ब्लॉक tहो जाता है। यह धागे को प्रभावित नहीं करता है t
मार्क बायर्स

1
धन्यवाद। =] यूनी में समानतावाद का अध्ययन किया, लेकिन यह एकमात्र ऐसी चीज थी जिसे मैंने सीखने के लिए संघर्ष किया! शुक्र है कि मुझे अब इसका उपयोग करने की आवश्यकता नहीं है या जब मैं इसे बहुत जटिल नहीं करता हूं या कोई साझा संसाधन नहीं हैं और ब्लॉकिंग महत्वपूर्ण नहीं है
जेम्स वेबस्टर

1
@ 4r1y4n क्या प्रदान किया गया कोड वास्तव में समानांतर है, इस बात पर निर्भर करता है कि आप इसके साथ क्या करने की कोशिश कर रहे हैं, और शामिल किए गए थ्रेड्स का उपयोग करके पूरे संग्रह में फैले डेटा को एकत्र करने से अधिक संबंधित है। आप थ्रेड्स ज्वाइन कर रहे हैं, जिसका संभावित मतलब "जॉइनिंग" डेटा है। इसके अलावा, समानता का मतलब जरूरी नहीं है कि समवर्ती हो। यह CPU पर निर्भर है। यह बहुत अच्छी तरह से हो सकता है कि थ्रेड समानांतर में चल रहे हैं, लेकिन गणना जो कुछ भी हो रहा है, वह अंतर्निहित सीपीयू द्वारा निर्धारित किया जाता है।

22

विभिन्न समाधानों पर एक नजर।

  1. join()एपीआई को जावा के शुरुआती संस्करणों में पेश किया गया है। कुछ अच्छे विकल्प JDK 1.5 रिलीज के बाद से इस समवर्ती पैकेज के साथ उपलब्ध हैं ।

  2. ExecutorService # invokeAll ()

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

    कोड उदाहरण के लिए इस संबंधित एसई प्रश्न का संदर्भ लें:

    सभी थ्रेड पूल को अपना काम करने देने के लिए invokeAll () का उपयोग कैसे करें?

  3. CountDownLatch

    एक सिंक्रनाइज़ेशन सहायता जो एक या अधिक थ्रेड्स को प्रतीक्षा करने की अनुमति देती है जब तक कि अन्य थ्रेड्स में किए गए संचालन का एक सेट पूरा नहीं हो जाता।

    एक CountDownLatch एक दिए गए गिनती के साथ शुरू होता है। प्रतीक्षा पद्धति तब तक अवरुद्ध होती है जब तक कि countDown()विधि के इनवोकेशन के कारण वर्तमान गिनती शून्य तक नहीं पहुंच जाती है , जिसके बाद सभी प्रतीक्षा थ्रेड जारी किए जाते हैं और प्रतीक्षा के तुरंत बाद के सभी इनवोकेशन तुरंत लौट आते हैं। यह एक-शॉट वाली घटना है - गणना को रीसेट नहीं किया जा सकता है। यदि आपको एक संस्करण की आवश्यकता है जो गिनती को रीसेट करता है, तो एक साइक्लिक बैरियर का उपयोग करने पर विचार करें ।

    के उपयोग के लिए इस प्रश्न का संदर्भ लें CountDownLatch

    एक धागे की प्रतीक्षा कैसे करें जो यह स्वयं का धागा है?

  4. ForkJoinPool या newWorkStealingPool () में निष्पादकों

  5. सबमिट करने के बाद बनाई गई सभी भविष्य की वस्तुओं के माध्यम से IterateExecutorService


11

इसके अलावा Thread.join()अन्य लोगों ने सुझाव दिया, जावा 5 निष्पादक ढांचे की शुरुआत की। वहां आप Threadवस्तुओं के साथ काम नहीं करते हैं। इसके बजाय, आप अपनी Callableया Runnableवस्तुओं को एक निष्पादक को प्रस्तुत करते हैं । एक विशेष निष्पादक है जो कई कार्यों को अंजाम देने के लिए होता है और अपने परिणामों को क्रम से बाहर लौटाता है। वह है ExecutorCompletionService:

ExecutorCompletionService executor;
for (..) {
    executor.submit(Executors.callable(yourRunnable));
}

तब आप बार-बार कॉल कर सकते हैं take()जब तक कि Future<?>लौटने के लिए और अधिक ऑब्जेक्ट न हों , जिसका अर्थ है कि सभी पूर्ण हो गए हैं।


आपके परिदृश्य के आधार पर एक और बात प्रासंगिक हो सकती है CyclicBarrier

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


यह करीब है, लेकिन मैं अभी भी समायोजन के एक जोड़े करना चाहते हैं। executor.submitएक रिटर्न Future<?>। मैं इन फ्यूचर्स को एक सूची में जोड़ दूंगा, और फिर getप्रत्येक भविष्य पर कॉल करने वाली सूची के माध्यम से लूप करूंगा ।
रे

इसके अलावा, आप एक कंस्ट्रक्टर का उपयोग कर सकते हैं Executors, उदाहरण के लिए, Executors.newCachedThreadPool(या समान)
रे

10

एक और संभावना CountDownLatchवस्तु है, जो सरल स्थितियों के लिए उपयोगी है: चूंकि आप पहले से ही थ्रेड्स की संख्या जानते हैं, आप इसे संबंधित गणना के साथ प्रारंभ करते हैं, और प्रत्येक थ्रेड के लिए ऑब्जेक्ट का संदर्भ पास करते हैं।
अपने कार्य के पूरा होने पर, प्रत्येक थ्रेड कॉल करता है CountDownLatch.countDown()जो आंतरिक काउंटर को घटाता है। मुख्य धागा, अन्य सभी को शुरू करने के बाद, CountDownLatch.await()अवरुद्ध कॉल करना चाहिए । आंतरिक काउंटर 0 पर पहुंचते ही इसे जारी कर दिया जाएगा।

ध्यान दें कि इस वस्तु के साथ, एक InterruptedExceptionभी फेंक दिया जा सकता है।


8

तुम करो

for (Thread t : new Thread[] { th1, th2, th3, th4, th5 })
    t.join()

लूप के लिए इसके बाद, आप सुनिश्चित कर सकते हैं कि सभी थ्रेड्स ने अपने काम समाप्त कर लिए हैं।


6

प्रतीक्षा करें / थ्रेड मेन को तब तक ब्लॉक करें जब तक कि कुछ अन्य धागे अपना काम पूरा नहीं कर लेते।

जैसा @Ravindra babuकि कहा गया है कि इसे विभिन्न तरीकों से हासिल किया जा सकता है, लेकिन उदाहरणों के साथ।

  • java.lang.Thread। join () चूंकि: 1.0

    public static void joiningThreads() throws InterruptedException {
        Thread t1 = new Thread( new LatchTask(1, null), "T1" );
        Thread t2 = new Thread( new LatchTask(7, null), "T2" );
        Thread t3 = new Thread( new LatchTask(5, null), "T3" );
        Thread t4 = new Thread( new LatchTask(2, null), "T4" );
    
        // Start all the threads
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        // Wait till all threads completes
        t1.join();
        t2.join();
        t3.join();
        t4.join();
    }
  • java.util.concurrent.CountDownLatch चूंकि: 1.5

    • .countDown() «कुंडी समूह की गिनती में कमी।
    • .await() «प्रतीक्षा की विधियाँ तब तक अवरुद्ध होती हैं जब तक कि वर्तमान गिनती शून्य तक नहीं पहुँच जाती।

    यदि आपने बनाया है latchGroupCount = 4तो countDown()गिनती 0. बनाने के लिए 4 बार कॉल किया जाना चाहिए, ताकि await()ब्लॉकिंग थ्रेड्स को रिलीज़ किया जा सके।

    public static void latchThreads() throws InterruptedException {
        int latchGroupCount = 4;
        CountDownLatch latch = new CountDownLatch(latchGroupCount);
        Thread t1 = new Thread( new LatchTask(1, latch), "T1" );
        Thread t2 = new Thread( new LatchTask(7, latch), "T2" );
        Thread t3 = new Thread( new LatchTask(5, latch), "T3" );
        Thread t4 = new Thread( new LatchTask(2, latch), "T4" );
    
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    
        //latch.countDown();
    
        latch.await(); // block until latchGroupCount is 0.
    }

थ्रेडेड क्लास का उदाहरण कोड LatchTask। दृष्टिकोण उपयोग joiningThreads(); और latchThreads();मुख्य विधि से परीक्षण करने के लिए ।

class LatchTask extends Thread {
    CountDownLatch latch;
    int iterations = 10;
    public LatchTask(int iterations, CountDownLatch latch) {
        this.iterations = iterations;
        this.latch = latch;
    }

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");

        for (int i = 0; i < iterations; i++) {
            System.out.println(threadName + " : " + i);
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task");
        // countDown() « Decrements the count of the latch group.
        if(latch != null)
            latch.countDown();
    }
}
  • साइक्लिक बैरियर एक तुल्यकालन सहायता है जो थ्रेड्स के एक सेट को एक दूसरे के लिए एक सामान्य बाधा बिंदु तक पहुंचने के लिए प्रतीक्षा करने की अनुमति देता है। साइक्लिक बैरियर उन थ्रेड्स के एक निश्चित आकार की पार्टी को शामिल करने वाले कार्यक्रमों में उपयोगी होते हैं जो कभी-कभी एक-दूसरे का इंतजार करते हैं। बैरियर को चक्रीय कहा जाता है क्योंकि वेटिंग थ्रेड्स जारी होने के बाद इसे फिर से इस्तेमाल किया जा सकता है।
    CyclicBarrier barrier = new CyclicBarrier(3);
    barrier.await();
    उदाहरण के लिए इस समवर्ती_पार्श्विकांतर श्रेणी को देखें ।

  • निष्पादनकर्ता ढांचा: हम उपयोग कर सकते हैं ExecutorService एक धागा पूल बनाने के लिए, और भविष्य के साथ अतुल्यकालिक कार्यों की प्रगति ट्रैक करता है।

    • submit(Runnable), submit(Callable)जो फ्यूचर ऑब्जेक्ट लौटाते हैं। future.get()फ़ंक्शन का उपयोग करके हम मुख्य धागे को तब तक अवरुद्ध कर सकते हैं जब तक कि काम करने वाले धागे अपना काम पूरा नहीं कर लेते।

    • invokeAll(...) - भविष्य की वस्तुओं की एक सूची देता है जिसके माध्यम से आप प्रत्येक कॉल करने योग्य के निष्पादन के परिणाम प्राप्त कर सकते हैं।

एक्सेप्टर रनवेबल, एक्ज़ीकलर फ्रेमवर्क के साथ कॉल करने योग्य का उपयोग करने का उदाहरण ढूंढें


@यह सभी देखें


4

थ्रेड-ऑब्जेक्ट्स को कुछ संग्रह में संग्रहित करें (जैसे सूची या सेट), फिर थ्रेड शुरू होने पर संग्रह के माध्यम से लूप करें और थ्रेड्स पर जॉइन () कॉल करें ।



2

हालांकि ओपी की समस्या के लिए प्रासंगिक नहीं है, यदि आप तुल्यकालन में रुचि रखते हैं (अधिक सटीक रूप से, ठीक एक धागा के साथ एक अनुलोम-विलोम), तो आप एक एक्सचेंजर का उपयोग कर सकते हैं

मेरे मामले में, मुझे तब तक माता-पिता के धागे को थामने की ज़रूरत थी जब तक कि बच्चे के धागे ने कुछ नहीं किया, उदाहरण के लिए इसका आरंभ पूरा हुआ। एक CountDownLatch भी अच्छा काम करता है।


1

स्थिति और पूर्णता सहित कई थ्रेड्स को प्रबंधित करने के लिए एक निष्पादक सेवा का उपयोग किया जा सकता है। Http://programmingexamples.wikidot.com/executorservice देखें


1

यह कोशिश करो, काम करेंगे।

  Thread[] threads = new Thread[10];

  List<Thread> allThreads = new ArrayList<Thread>();

  for(Thread thread : threads){

        if(null != thread){

              if(thread.isAlive()){

                    allThreads.add(thread);

              }

        }

  }

  while(!allThreads.isEmpty()){

        Iterator<Thread> ite = allThreads.iterator();

        while(ite.hasNext()){

              Thread thread = ite.next();

              if(!thread.isAlive()){

                   ite.remove();
              }

        }

   }

1

मुझे एक समान समस्या थी और जावा 8 समानांतरस्ट्रीम का उपयोग करके समाप्त हुई।

requestList.parallelStream().forEach(req -> makeRequest(req));

यह सुपर सरल और पठनीय है। दृश्यों के पीछे यह डिफ़ॉल्ट JVM के कांटे से जुड़ने वाले पूल का उपयोग कर रहा है, जिसका अर्थ है कि यह जारी रखने से पहले सभी थ्रेड के समाप्त होने का इंतजार करेगा। मेरे मामले के लिए यह एक साफ समाधान था, क्योंकि यह मेरे आवेदन में केवल समानांतर स्ट्र्रीम था। यदि आपके पास एक साथ एक से अधिक समानांतर धारा चल रही है, तो कृपया नीचे दिए गए लिंक को पढ़ें।

समानांतर धाराओं के बारे में अधिक जानकारी यहाँ


0

मौजूदा जवाब में कहा गया है कि join()प्रत्येक धागा।

लेकिन थ्रेड सरणी / सूची प्राप्त करने के कई तरीके हैं:

  • निर्माण पर एक सूची में थ्रेड जोड़ें।
  • ThreadGroupथ्रेड्स को प्रबंधित करने के लिए उपयोग करें ।

निम्नलिखित कोड ThreadGruopदृष्टिकोण का उपयोग करेगा । यह पहले एक ग्रुप बनाता है, फिर जब प्रत्येक थ्रेड को कंस्ट्रक्टर में समूह निर्दिष्ट करता है, तो बाद में थ्रेड सरणी प्राप्त कर सकता हैThreadGroup.enumerate()


कोड

SyncBlockLearn.java

import org.testng.Assert;
import org.testng.annotations.Test;

/**
 * synchronized block - learn,
 *
 * @author eric
 * @date Apr 20, 2015 1:37:11 PM
 */
public class SyncBlockLearn {
    private static final int TD_COUNT = 5; // thread count
    private static final int ROUND_PER_THREAD = 100; // round for each thread,
    private static final long INC_DELAY = 10; // delay of each increase,

    // sync block test,
    @Test
    public void syncBlockTest() throws InterruptedException {
        Counter ct = new Counter();
        ThreadGroup tg = new ThreadGroup("runner");

        for (int i = 0; i < TD_COUNT; i++) {
            new Thread(tg, ct, "t-" + i).start();
        }

        Thread[] tArr = new Thread[TD_COUNT];
        tg.enumerate(tArr); // get threads,

        // wait all runner to finish,
        for (Thread t : tArr) {
            t.join();
        }

        System.out.printf("\nfinal count: %d\n", ct.getCount());
        Assert.assertEquals(ct.getCount(), TD_COUNT * ROUND_PER_THREAD);
    }

    static class Counter implements Runnable {
        private final Object lkOn = new Object(); // the object to lock on,
        private int count = 0;

        @Override
        public void run() {
            System.out.printf("[%s] begin\n", Thread.currentThread().getName());

            for (int i = 0; i < ROUND_PER_THREAD; i++) {
                synchronized (lkOn) {
                    System.out.printf("[%s] [%d] inc to: %d\n", Thread.currentThread().getName(), i, ++count);
                }
                try {
                    Thread.sleep(INC_DELAY); // wait a while,
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.printf("[%s] end\n", Thread.currentThread().getName());
        }

        public int getCount() {
            return count;
        }
    }
}

मुख्य धागा समूह में सभी थ्रेड्स के समाप्त होने तक प्रतीक्षा करेगा।


0

मैंने कुछ थ्रेड्स के खत्म होने का इंतजार करने के लिए एक छोटा सहायक तरीका बनाया:

public static void waitForThreadsToFinish(Thread... threads) {
        try {
            for (Thread thread : threads) {
                thread.join();
            }
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

-1

अपने मुख्य सूत्र में इसका उपयोग करें: जबकि ((निष्पादक। सुस्पष्ट) (); निष्पादक सेवा से सभी थ्रेड प्रारंभ करने के बाद कोड की इस पंक्ति को रखें। यह केवल मुख्य थ्रेड को प्रारंभ करेगा जब निष्पादकों द्वारा शुरू किए गए सभी थ्रेड समाप्त हो जाएंगे। निष्पादक को कॉल करें। शटडाउन (); उपरोक्त लूप से पहले।


यह सक्रिय प्रतीक्षा है, जिसके कारण सीपीयू लगातार एक खाली लूप चलाएगा। बहुत बेकार।
एडम मिशालिक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.