ExecutorService, सभी कार्यों को समाप्त करने के लिए इंतजार कैसे करें


198

ExecutorServiceखत्म करने के सभी कार्यों के लिए इंतजार करने का सबसे सरल तरीका क्या है ? मेरा काम मुख्य रूप से कम्प्यूटेशनल है, इसलिए मैं बस बड़ी संख्या में नौकरियां चलाना चाहता हूं - प्रत्येक कोर पर। अभी मेरा सेटअप इस तरह दिखता है:

ExecutorService es = Executors.newFixedThreadPool(2);
for (DataTable singleTable : uniquePhrases) {   
    es.execute(new ComputeDTask(singleTable));
}
try{
    es.wait();
} 
catch (InterruptedException e){
    e.printStackTrace();
}

ComputeDTaskलागू करने योग्य। यह कार्यों को सही ढंग से निष्पादित करने के लिए प्रकट होता है, लेकिन कोड के wait()साथ क्रैश हो जाता है IllegalMonitorStateException। यह अजीब है, क्योंकि मैंने कुछ खिलौना उदाहरणों के साथ खेला और यह काम करने के लिए दिखाई दिया।

uniquePhrasesहजारों तत्वों के कई दसियों शामिल हैं। क्या मुझे दूसरी विधि का उपयोग करना चाहिए? मैं यथासंभव सरल कुछ ढूंढ रहा हूं


1
अगर आप प्रतीक्षा का उपयोग करना चाहते हैं (उत्तर आपको बताते हैं): आपको हमेशा ऑब्जेक्ट (इस मामले में es) को सिंक्रनाइज़ करने की आवश्यकता होती है जब आप इसके लिए इंतजार करना चाहते हैं - लॉक स्वचालित रूप से प्रतीक्षा करते समय जारी किया जाएगा
मिही

13
Executors.newFixedThreadPool(System.getRuntime().availableProcessors());

7
Runtime.getRuntime () availableProcessors ()।;
विक गमोव

[यह] [१] एक दिलचस्प विकल्प है .. [१]: stackoverflow.com/questions/1250643/…
रोज्ज़न पेट्रुसेस्कु '

1
यदि आप CountDownLatch का उपयोग करना चाहते हैं, तो यह उदाहरण कोड है: stackoverflow.com/a/44127101/4069305
Tuan Pham

जवाबों:


213

सबसे सरल दृष्टिकोण का उपयोग करना है ExecutorService.invokeAll()जो वह करता है जो आप एक-लाइनर में चाहते हैं। आपके संसर्ग में, आपको ComputeDTaskलागू करने के लिए संशोधित या लपेटने की आवश्यकता होगी Callable<>, जो आपको थोड़ा अधिक लचीलापन दे सकता है। संभवत: आपके ऐप में इसका एक सार्थक कार्यान्वयन है Callable.call(), लेकिन इसका उपयोग न करने पर इसे लपेटने का एक तरीका है Executors.callable()

ExecutorService es = Executors.newFixedThreadPool(2);
List<Callable<Object>> todo = new ArrayList<Callable<Object>>(singleTable.size());

for (DataTable singleTable: uniquePhrases) { 
    todo.add(Executors.callable(new ComputeDTask(singleTable))); 
}

List<Future<Object>> answers = es.invokeAll(todo);

जैसा कि दूसरों ने बताया है, invokeAll()यदि आप उचित समय के संस्करण का उपयोग कर सकते हैं । इस उदाहरण में, s answersका एक समूह शामिल है Futureजो nulls को लौटाएगा (देखें की परिभाषा Executors.callable()। संभवतः आप जो करना चाहते हैं वह एक मामूली रिफैक्टरिंग है ताकि आप एक उपयोगी उत्तर वापस पा सकें, या अंतर्निहित का संदर्भ दे ComputeDTaskसकें, लेकिन मैं कर सकता हूं 'अपने उदाहरण से मत बताओ।

यदि यह स्पष्ट नहीं है, तो ध्यान दें कि invokeAll()सभी कार्य पूर्ण होने तक वापस नहीं आएंगे। (यानी, Futureआपके answersसंग्रह के सभी एस .isDone()यदि पूछा जाएगा तो रिपोर्ट करेंगे ।) यह सभी मैनुअल शटडाउन, वेइटमार्टमेशन इत्यादि से बचा जाता है ... और ExecutorServiceयदि वांछित हो, तो आपको कई चक्रों के लिए इसे फिर से उपयोग करने की अनुमति देता है ।

SO पर कुछ संबंधित प्रश्न हैं:

इनमें से कोई भी आपके प्रश्न के लिए कड़ाई से ऑन-पॉइंट नहीं है, लेकिन वे इस बारे में थोड़ा रंग प्रदान करते हैं कि लोग कैसे सोचते हैं Executor/ ExecutorServiceउनका उपयोग किया जाना चाहिए।


9
यह एकदम सही है यदि आप एक बैच में अपनी सभी नौकरियों को जोड़ रहे हैं और आप Callables की सूची में हैंग हो जाते हैं, लेकिन यदि आप ExecutorService.submit () को कॉलबैक या इवेंट-लूप स्थिति में बुला रहे हैं तो यह काम नहीं करेगा।
डेस्टी सेप

2
मुझे लगता है कि यह उल्लेखनीय है कि शटडाउन () को तब भी बुलाया जाना चाहिए जब एक्सक्यूसोर सर्विस को अब ज़रूरत नहीं है, अन्यथा थ्रेड कभी भी समाप्त नहीं होंगे (उन मामलों को छोड़कर जब CorePoolSize = 0 या allowCoreThreadTime.ut = true)।
14:29 पर जॉन

गजब का! मुझे इसकी ही खोज थी। उत्तर साझा करने के लिए बहुत बहुत धन्यवाद। मुझे यह कोशिश करने दो।
मोहम्मदनाउल्ला

59

यदि आप सभी कार्यों को पूरा करने के लिए इंतजार करना चाहते हैं, तो shutdownइसके बजाय विधि का उपयोग करें wait। तो इसके साथ पालन करें awaitTermination

इसके अलावा, आप Runtime.availableProcessorsहार्डवेयर थ्रेड की संख्या प्राप्त करने के लिए उपयोग कर सकते हैं ताकि आप अपने थ्रेडपूल को ठीक से प्रारंभ कर सकें।


27
शटडाउन () नए कार्यों को स्वीकार करने से एक्सेलोरसेवर को रोक देता है और निष्क्रिय कर्मचारी थ्रेड्स को बंद कर देता है। शटडाउन के पूरा होने के लिए इंतजार करने के लिए निर्दिष्ट नहीं किया गया है और थ्रेडपूल एक्ज़ीक्यूटर में कार्यान्वयन की प्रतीक्षा नहीं करता है।
एलेन ओ'डे

1
@ साभार - धन्यवाद मुझे वाक्पटुता का उल्लेख करना चाहिए था। फिक्स्ड।
एन.जी.

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

2
awaitTerminationपैरामीटर के रूप में टाइमआउट समय की आवश्यकता होती है। हालांकि यह संभव है कि सभी समय समाप्त होने तक प्रतीक्षा करने के लिए एक परिमित समय प्रदान करें और इसके चारों ओर एक लूप रखें, मैं सोच रहा था कि क्या अधिक सुरुचिपूर्ण समाधान था।
अभिषेक एस

1
आप सही हैं, लेकिन इस उत्तर को देखें - stackoverflow.com/a/1250655/263895 - आप इसे हमेशा एक अविश्वसनीय रूप से लंबे समय तक दे सकते हैं
NG।

48

यदि सभी कार्यों ExecutorServiceको पूरा करने के लिए इंतजार करना आपका लक्ष्य ठीक नहीं है, लेकिन जब तक कि कार्यों का एक विशिष्ट बैच पूरा नहीं हो जाता है, तब तक आप प्रतीक्षा कर सकते हैं CompletionService- विशेष रूप से, ए ExecutorCompletionService

विचार यह है कि ExecutorCompletionServiceअपने रैपिंग बनाएं Executor, कुछ ज्ञात कार्यों को इसके माध्यम से सबमिट करें , फिर पूर्ण कतार से या तो (जो ब्लॉक) या (जो नहीं करता है ) का उपयोग करके परिणामों की एक ही संख्या आकर्षित करें । एक बार जब आप अपने द्वारा सबमिट किए गए कार्यों के अनुरूप सभी अपेक्षित परिणाम निकाल लेते हैं, तो आप जानते हैं कि वे सभी काम कर चुके हैं।CompletionServicetake()poll()

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

कुछ उदाहरण दिखाCompletionService रहे हैं कि कैसे अभ्यास में जावा कॉनक्यूरेक्टीव बुक का उपयोग करें


यह मेरे उत्तर के लिए एक अच्छा प्रतिवाद है - मैं कहूँगा कि इस प्रश्न का सीधा उत्तर इनवोक है (); लेकिन @ यह सही है जब ES के लिए नौकरियों के समूह को जमा करने और उन्हें पूरा करने के लिए प्रतीक्षा कर रहा है ... --JA
andersoj

@ om-nom-nom, लिंक अपडेट करने के लिए धन्यवाद। मुझे यह देखकर खुशी हुई कि उत्तर अभी भी उपयोगी है।
सेह

1
अच्छा जवाब है, मैं के बारे में पता नहीं थाCompletionService
विक

1
यह उपयोग करने के लिए दृष्टिकोण है, यदि आप किसी मौजूदा एक्सेकॉर्स सर्विस को बंद नहीं करना चाहते हैं, लेकिन बस कार्यों का एक बैच जमा करना चाहते हैं, और पता है कि वे कब समाप्त हो रहे हैं।
टूलमेकरसेव

11

यदि आप निष्पादक सेवा को समाप्त करने के लिए प्रतीक्षा करना चाहते हैं, shutdown()तो कॉल करें और फिर, वेटटर्मिनेशन (यूनिट, यूनिट टाइप) , जैसे awaitTermination(1, MINUTE)। ExecutorService अपने स्वयं के मॉनिटर पर ब्लॉक नहीं करता है, इसलिए आप इसका उपयोग नहीं कर सकते हैं waitआदि।


मुझे लगता है कि यह प्रतीक्षारत है।
एन.जी.

@ एसबी - धन्यवाद - मुझे लगता है कि मेरी याददाश्त गिरने योग्य है! मैंने नाम अपडेट किया है और सुनिश्चित करने के लिए एक लिंक जोड़ा है।
mdma

"हमेशा के लिए" इंतजार करने के लिए, इसे awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); stackoverflow.com/a/1250655/32453 की
rogerdpack

मुझे लगता है कि यह सबसे आसान तरीका है
शेरविन असगरी

1
@MosheElisha, क्या आपको यकीन है? docs.oracle.com/javase/8/docs/api/java/util/concurrent/… कहते हैं कि एक क्रमबद्ध बंद शुरू होता है जिसमें पहले प्रस्तुत कार्यों को निष्पादित किया जाता है, लेकिन कोई नया कार्य स्वीकार नहीं किया जाएगा।
२०:०२ पर Jaime Hablutzel

7

आप एक निश्चित अंतराल पर समाप्त होने के लिए नौकरियों की प्रतीक्षा कर सकते हैं:

int maxSecondsPerComputeDTask = 20;
try {
    while (!es.awaitTermination(uniquePhrases.size() * maxSecondsPerComputeDTask, TimeUnit.SECONDS)) {
        // consider giving up with a 'break' statement under certain conditions
    }
} catch (InterruptedException e) {
    throw new RuntimeException(e);    
}

या आप ExecutorService का उपयोग कर सकते हैं । सबमिट करें ( Runnable ) और भविष्य की उन वस्तुओं को इकट्ठा करें जिन्हें वे लौटाते हैं और कॉल प्राप्त करते हैं ( प्रत्येक ) बारी-बारी से उनका इंतजार करते हैं।

ExecutorService es = Executors.newFixedThreadPool(2);
Collection<Future<?>> futures = new LinkedList<<Future<?>>();
for (DataTable singleTable : uniquePhrases) {
    futures.add(es.submit(new ComputeDTask(singleTable)));
}
for (Future<?> future : futures) {
   try {
       future.get();
   } catch (InterruptedException e) {
       throw new RuntimeException(e);
   } catch (ExecutionException e) {
       throw new RuntimeException(e);
   }
}

ठीक से संभालने के लिए InterruptedException बेहद महत्वपूर्ण है। यह वह है जो आपको या आपके पुस्तकालय के उपयोगकर्ताओं को एक लंबी प्रक्रिया को सुरक्षित रूप से समाप्त करने देता है।


6

महज प्रयोग करें

latch = new CountDownLatch(noThreads)

प्रत्येक धागे में

latch.countDown();

और बाधा के रूप में

latch.await();

6

IllegalMonitorStateException का मूल कारण :

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

आपके कोड से, आपने लॉक के मालिक के बिना ExecutorService पर बस इंतजार () कहा है।

नीचे कोड तय करेगा IllegalMonitorStateException

try 
{
    synchronized(es){
        es.wait(); // Add some condition before you call wait()
    }
} 

सभी कार्यों को पूरा करने के लिए नीचे दिए गए दृष्टिकोणों में से एक का पालन करें, जिसे प्रस्तुत किया गया है ExecutorService

  1. सभी Futureकार्यों से प्राप्त submitकरें ExecutorServiceऔर ऑब्जेक्ट get()पर कॉल को अवरुद्ध करने के साथ स्थिति की जांच करेंFuture

  2. पर invokeAll का उपयोग करनाExecutorService

  3. CountDownLatch का उपयोग करना

  4. ForkJoinPool या newWorkStealingPool का उपयोग करना Executors(जावा 8 से)

  5. Oracle प्रलेखन पृष्ठ में सिफारिश के रूप में पूल बंद

    void shutdownAndAwaitTermination(ExecutorService pool) {
       pool.shutdown(); // Disable new tasks from being submitted
       try {
       // Wait a while for existing tasks to terminate
       if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
           pool.shutdownNow(); // Cancel currently executing tasks
           // Wait a while for tasks to respond to being cancelled
           if (!pool.awaitTermination(60, TimeUnit.SECONDS))
           System.err.println("Pool did not terminate");
       }
    } catch (InterruptedException ie) {
         // (Re-)Cancel if current thread also interrupted
         pool.shutdownNow();
         // Preserve interrupt status
         Thread.currentThread().interrupt();
    }

    यदि आप जब आप विकल्प 1 से 4 के बजाय विकल्प 5 का उपयोग कर रहे हैं, तो आप सभी कार्यों को पूरा करने के लिए अनुग्रहपूर्वक प्रतीक्षा करना चाहते हैं

    if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {

    सेवा

    एक while(condition)है जो हर 1 मिनट के लिए जाँच करता है।


6

आप ExecutorService.invokeAllविधि का उपयोग कर सकते हैं , यह सभी कार्य निष्पादित करेगा और तब तक प्रतीक्षा करेगा जब तक कि सभी सूत्र अपना कार्य पूरा नहीं कर लेते।

यहां पूरा जावदोक है

टाइमआउट को निर्दिष्ट करने के लिए आप इस विधि का ओवरलोड संस्करण भी ले सकते हैं।

यहाँ नमूना कोड के साथ है ExecutorService.invokeAll

public class Test {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService service = Executors.newFixedThreadPool(3);
        List<Callable<String>> taskList = new ArrayList<>();
        taskList.add(new Task1());
        taskList.add(new Task2());
        List<Future<String>> results = service.invokeAll(taskList);
        for (Future<String> f : results) {
            System.out.println(f.get());
        }
    }

}

class Task1 implements Callable<String> {
    @Override
    public String call() throws Exception {
        try {
            Thread.sleep(2000);
            return "Task 1 done";
        } catch (Exception e) {
            e.printStackTrace();
            return " error in task1";
        }
    }
}

class Task2 implements Callable<String> {
    @Override
    public String call() throws Exception {
        try {
            Thread.sleep(3000);
            return "Task 2 done";
        } catch (Exception e) {
            e.printStackTrace();
            return " error in task2";
        }
    }
}

3

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

अपने मुख्य कार्यक्रम में, मैं केवल निम्नलिखित की तरह कुछ लिखना चाहता हूं, जहां Crawlerथ्रेड का एक गुच्छा नियंत्रित करता है।

Crawler c = new Crawler();
c.schedule(seedDocument); 
c.waitUntilCompletion()

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

मुझे जेवीएम में कुछ भी नहीं मिला जो मुझे लगा कि थोड़ा आश्चर्यचकित है। इसलिए मैंने एक वर्ग लिखा, ThreadPoolजो या तो सीधे या उपवर्ग का उपयोग कर सकता है ताकि डोमेन के लिए उपयुक्त तरीके जोड़ सकें, जैसे schedule(Document)। आशा करता हूँ की ये काम करेगा!

थ्रेडपूल जावदोक | Maven


डॉक लिंक मर चुका है
मेंटी_कोर

@ मंटी_कोर - धन्यवाद, अद्यतन।
एड्रियन स्मिथ

2

संग्रह में सभी सूत्र जोड़ें और इसका उपयोग करके सबमिट करें invokeAll। यदि आप invokeAllविधि का उपयोग कर सकते हैं ExecutorService, तो JVM अगली पंक्ति में आगे नहीं बढ़ेगा, जब तक कि सभी धागे पूर्ण नहीं हो जाते।

यहाँ एक अच्छा उदाहरण है: InvokeAll ExecutorService के माध्यम से


1

अपने कार्यों को रनर में जमा करें और फिर इस तरह से WaitTillDone () विधि की प्रतीक्षा करें:

Runner runner = Runner.runner(2);

for (DataTable singleTable : uniquePhrases) {

    runner.run(new ComputeDTask(singleTable));
}

// blocks until all tasks are finished (or failed)
runner.waitTillDone();

runner.shutdown();

इसका उपयोग करने के लिए इस ढाल / मावेन निर्भरता को जोड़ें: 'com.github.matejtymes:javafixes:1.0'

अधिक जानकारी के लिए यहाँ देखें: https://github.com/MatejTymes/JavaFixes या यहाँ: http://matejtymes.blogspot.com/2016/04/executor-that-notify-you-when-w.html



0

मैं बस निष्पादक के लिए एक निर्दिष्ट समय समाप्ति के साथ समाप्त होने की प्रतीक्षा करूंगा जो आपको लगता है कि यह कार्यों को पूरा करने के लिए उपयुक्त है।

 try {  
         //do stuff here 
         exe.execute(thread);
    } finally {
        exe.shutdown();
    }
    boolean result = exe.awaitTermination(4, TimeUnit.HOURS);
    if (!result)

    {
        LOGGER.error("It took more than 4 hour for the executor to stop, this shouldn't be the normal behaviour.");
    }

0

आप की तरह लगता है ForkJoinPoolऔर कार्यों को निष्पादित करने के लिए वैश्विक पूल का उपयोग करें।

public static void main(String[] args) {
    // the default `commonPool` should be sufficient for many cases.
    ForkJoinPool pool = ForkJoinPool.commonPool(); 
    // The root of your task that may spawn other tasks. 
    // Make sure it submits the additional tasks to the same executor that it is in.
    Runnable rootTask = new YourTask(pool); 
    pool.execute(rootTask);
    pool.awaitQuiescence(...);
    // that's it.
}

सुंदरता वह जगह है pool.awaitQuiescenceजहां विधि अपने कार्यों को निष्पादित करने के लिए कॉलर के थ्रेड का उपयोग करेगी और फिर वापस आ जाएगी जब यह वास्तव में खाली हो।

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