วิธีการรอให้เธรดทั้งหมดเสร็จสิ้นโดยใช้ ExecutorService


381

ฉันต้องทำงานบางอย่างทีละ 4 อย่างเช่นนี้:

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
    taskExecutor.execute(new MyTask());
}
//...wait for completion somehow

ฉันจะได้รับการแจ้งเตือนได้อย่างไรเมื่อทั้งหมดเสร็จสมบูรณ์ สำหรับตอนนี้ฉันไม่สามารถคิดอะไรได้ดีไปกว่าการตั้งตัวนับโกลบอลบางตัวและลดมันลงที่ส่วนท้ายของทุกภารกิจ หรือรับรายการ Futures และใน infinite loop monitor isDone สำหรับพวกเขาทั้งหมด อะไรคือวิธีแก้ปัญหาที่ดีกว่าที่ไม่เกี่ยวข้องกับการวนซ้ำไม่สิ้นสุด?

ขอบคุณ

คำตอบ:


446

โดยพื้นฐานแล้วExecutorServiceคุณจะโทรshutdown()แล้วawaitTermination():

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
  taskExecutor.execute(new MyTask());
}
taskExecutor.shutdown();
try {
  taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
  ...
}

9
ตรงนี้เป็นสิ่งปิด / awaitTermination มีความหมายสำหรับ
ด้านข

31
มันเป็นรูปแบบที่ดีถ้าการจัดการงานนี้เป็นเหตุการณ์ที่เกิดขึ้นครั้งเดียว หากทำสิ่งนี้ซ้ำหลายครั้งในช่วงรันไทม์เดียวกันมันไม่เหมาะสมเนื่องจากคุณจะต้องสร้างและทำลายเธรดซ้ำ ๆ ทุกครั้งที่เรียกใช้งาน
sjlee

44
ฉันกำลังมองหาเอกสารอย่างเป็นทางการใด ๆ ที่Long.MAX_VALUE, TimeUnit.NANOSECONDSเทียบเท่ากับการไม่มีการหมดเวลา
Sam Harwell

15
ฉันไม่อยากจะเชื่อเลยว่าคุณต้องใช้การปิดระบบเพื่อเข้าร่วมกับเธรดปัจจุบันทั้งหมด (หลังจากใช้การปิดระบบคุณจะไม่สามารถใช้ตัวเรียกใช้งานได้อีกเลย) แนะนำให้ใช้รายการของ Future's แทน ...
rogerdpack

20
@ SamHarwell ดูเอกสารjava.util.concurrentแพคเกจภายใต้หัวข้อ: หากต้องการรอ "ถาวร" คุณสามารถใช้ค่าของTimingLong.MAX_VALUE
beluchin

174

ใช้CountDownLatch :

CountDownLatch latch = new CountDownLatch(totalNumberOfTasks);
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
  taskExecutor.execute(new MyTask());
}

try {
  latch.await();
} catch (InterruptedException E) {
   // handle
}

และภายในงานของคุณ (ให้ลอง / ในที่สุด)

latch.countDown();

3
ไม่มี 4 งาน มี "งานบางอย่าง" ที่ทำได้ 4 รายการในแต่ละครั้ง
cletus

2
ขออภัยฉันเข้าใจผิดคำถาม ใช่จำนวนงานควรเป็นอาร์กิวเมนต์ของตัวสร้าง CountDownLatch
ChssPly76

3
ฉันพบว่าโซลูชันนี้มีความหรูหรามากกว่าผลิตภัณฑ์อื่น ๆ ดูเหมือนว่ามันถูกสร้างขึ้นเพื่อจุดประสงค์นี้และมันเรียบง่ายและตรงไปตรงมา
wvdschel

3
ถ้าคุณไม่ทราบจำนวนงานก่อนที่จะเริ่ม
cletus

11
@cletus - จากนั้นคุณไม่ได้ใช้ CountDownLatch :-) ทราบว่าฉันไม่เถียงว่าวิธีนี้ดีกว่าของคุณ แต่ผมพบว่าในสถานการณ์ในชีวิตจริงผมทำรู้จำนวนของงานการตั้งค่าสระด้ายไม่จำเป็นที่จะต้องกำหนดค่าต่อการใช้งานและสระว่ายน้ำที่สามารถนำกลับมาใช้ ดังนั้นฉันมักจะมีกลุ่มเธรดที่ถูกฉีดโดยสปริงและตั้งค่าให้เป็นแบบตัวอย่างและปิดด้วยตนเอง เท่านั้นเพื่อรอเธรดให้เสร็จดูเหมือนน้อยกว่าอุดมคติ
ChssPly76

82

ExecutorService.invokeAll() ทำเพื่อคุณ

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
List<Callable<?>> tasks; // your tasks
// invokeAll() returns when all tasks are complete
List<Future<?>> futures = taskExecutor.invokeAll(tasks);

ความยากลำบากมาถ้า / เมื่อคุณเริ่มหัวข้อ "4" ทีละชิ้นฉลาดแล้วเข้าร่วม / ให้เสร็จทั้ง 4 ...
rogerdpack

@rogerdpack: ฉันยังคงเรียนรู้ผู้บริหารและสิ่งนี้ แต่ในการตอบสนองต่อสิ่งที่คุณถาม 4 เธรดในเวลาเดียวกันควรไม่เป็นส่วนหนึ่งของงานแบตช์ซึ่งดำเนินการโดยใช้คำตอบข้างต้นหรือไม่
Mukul Goel

3
วิธีนี้จะใช้งานได้ก็ต่อเมื่อคุณทราบจำนวนงานก่อนมือ
Konstantin

3
ฉันคิดว่าเมื่อfuturesมีการส่งคืนงานยังไม่เสร็จสมบูรณ์ พวกเขาอาจเสร็จสมบูรณ์ในอนาคตและคุณจะมีลิงก์ไปยังผลลัพธ์ Futureนั่นเป็นเหตุผลว่าทำไมจึงเรียกว่า คุณมีวิธีFuture.get ()ซึ่งจะรอให้ภารกิจเสร็จสิ้นเพื่อให้ได้ผลลัพธ์
AlikElzin-kilaka

5
@ AlikElzin-kilaka อ้างอิงจาก JavaDocs (ลิงก์ในคำตอบ): "ดำเนินการตามภารกิจที่กำหนดส่งคืนรายการของ Futures ที่ถือสถานะและผลลัพธ์เมื่อเสร็จสมบูรณ์ Future.isDone () เป็นจริงสำหรับแต่ละองค์ประกอบของรายการที่ส่งคืน "
Hulk

47

คุณสามารถใช้รายการของฟิวเจอร์สได้เช่นกัน:

List<Future> futures = new ArrayList<Future>();
// now add to it:
futures.add(executorInstance.submit(new Callable<Void>() {
  public Void call() throws IOException {
     // do something
    return null;
  }
}));

จากนั้นเมื่อคุณต้องการที่จะเข้าร่วมกับพวกเขาทั้งหมดมันเป็นหลักเทียบเท่ากับการเข้าร่วมในแต่ละ (ด้วยสิทธิประโยชน์เพิ่มเติมที่มันยกข้อยกเว้นจากหัวข้อเด็กที่จะหลัก):

for(Future f: this.futures) { f.get(); }

โดยทั่วไปเคล็ดลับคือการเรียก. get () ในแต่ละครั้งในอนาคตแทนที่จะเรียกวนซ้ำไม่สิ้นสุด isDone () บน (ทั้งหมดหรือแต่ละคน) ดังนั้นคุณจึงมั่นใจได้ว่าจะ "ดำเนินการ" ผ่านและผ่านบล็อกนี้ทันทีที่กระทู้สุดท้ายเสร็จสิ้น ข้อแม้คือเนื่องจากการเรียก. get () re-raises ข้อยกเว้นถ้าหนึ่งในเธรดตายคุณจะยกระดับจากสิ่งนี้ก่อนที่เธรดอื่น ๆ จะเสร็จสมบูรณ์ [เพื่อหลีกเลี่ยงปัญหานี้คุณสามารถเพิ่มcatch ExecutionExceptionการรับสายได้ ] ข้อแม้อื่น ๆ คือมันเก็บการอ้างอิงถึงกระทู้ทั้งหมดดังนั้นหากพวกเขามีตัวแปรท้องถิ่นด้ายพวกเขาจะไม่ได้รับการรวบรวมจนกว่าหลังจากที่คุณได้รับผ่านบล็อกนี้ (แม้ว่าคุณอาจจะสามารถที่จะได้รับรอบนี้ถ้ามันกลายเป็นปัญหาโดยการลบ อนาคตจะไม่ใช้ ArrayList) หากคุณต้องการที่จะรู้ว่าอนาคต "เสร็จก่อน"https://stackoverflow.com/a/31885029/32453


3
หากต้องการทราบว่า "เสร็จสิ้นก่อนใคร" ให้ใช้ExecutorCompletionService.take: stackoverflow.com/a/11872604/199364
ToolmakerSteve

34

ใน Java8 คุณสามารถทำได้ด้วยCompletableFuture :

ExecutorService es = Executors.newFixedThreadPool(4);
List<Runnable> tasks = getTasks();
CompletableFuture<?>[] futures = tasks.stream()
                               .map(task -> CompletableFuture.runAsync(task, es))
                               .toArray(CompletableFuture[]::new);
CompletableFuture.allOf(futures).join();    
es.shutdown();

3
นี่เป็นทางออกที่หรูหรามาก
mattvonb

ExecutorService es = Executors.newFixedThreadPool(4); List< Future<?>> futures = new ArrayList<>(); for(Runnable task : taskList) { futures.add(es.submit(task)); } for(Future<?> future : futures) { try { future.get(); }catch(Exception e){ // do logging and nothing else } }
2862544

@AdamSkywalker กำลังรอการประกาศ () จำเป็นหลังจาก es.shutdown ()
gaurav

@gaurav เมื่อคุณเรียกการปิดบางงานอาจยังไม่เสร็จ ดังนั้นรอการประกาศจะบล็อกเธรดการโทรจนกว่าทุกอย่างจะเสร็จสิ้น ขึ้นอยู่กับว่าคุณต้องการรอผลลัพธ์ในชุดข้อความนี้หรือไม่
AdamSkywalker

@ AdamSkywalker คำตอบที่ดี ทำให้รู้สึกที่จะไม่โทร awaitTermination () ถ้าฉันไม่ต้องการรอผล
gaurav

26

แค่สองเซ็นต์ของฉัน ที่จะเอาชนะความต้องการของที่จะทราบจำนวนของงานก่อนคุณสามารถทำมันเป็นวิธีแฟชั่นเก่าโดยใช้ที่เรียบง่ายCountDownLatchSemaphore

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
int numberOfTasks=0;
Semaphore s=new Semaphore(0);
while(...) {
    taskExecutor.execute(new MyTask());
    numberOfTasks++;
}

try {
    s.aquire(numberOfTasks);
...

ในงานของคุณเพียงแค่โทรs.release()ตามที่คุณต้องการlatch.countDown();


เมื่อเห็นสิ่งนี้ฉันแรกที่สงสัยว่ามันจะเป็นปัญหาถ้ามีบางreleaseสายเกิดขึ้นก่อนที่จะacquireโทร แต่หลังจากอ่านเอกสารเซมาฟอร์ฉันเห็นว่าไม่เป็นไร
ToolmakerSteve

13

สายไปที่เกมเล็กน้อย แต่เพื่อความสมบูรณ์ ...

แทนที่จะ 'รอ' เพื่อให้งานทั้งหมดเสร็จสิ้นคุณสามารถคิดในแง่ของหลักการฮอลลีวูดว่า "อย่าโทรหาฉันฉันจะโทรหาคุณ" - เมื่อฉันทำเสร็จแล้ว ฉันคิดว่ารหัสที่ได้จะดูดีกว่านี้ ...

Guava มีเครื่องมือที่น่าสนใจที่จะทำให้สำเร็จ

ตัวอย่าง ::

ตัด ExecutorService ลงใน ListeningExecutorService ::

ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));

ส่งคอลเลกชันของ callables เพื่อดำเนินการ ::

for (Callable<Integer> callable : callables) {
  ListenableFuture<Integer> lf = service.submit(callable);
  // listenableFutures is a collection
  listenableFutures.add(lf)
});

ตอนนี้ส่วนสำคัญ:

ListenableFuture<List<Integer>> lf = Futures.successfulAsList(listenableFutures);

แนบการเรียกกลับไปยัง ListenableFuture ซึ่งคุณสามารถใช้เพื่อรับการแจ้งเตือนเมื่อฟิวเจอร์สทั้งหมดเสร็จสมบูรณ์ ::

        Futures.addCallback(lf, new FutureCallback<List<Integer>>() {
        @Override
        public void onSuccess(List<Integer> result) {
            log.info("@@ finished processing {} elements", Iterables.size(result));
            // do something with all the results
        }

        @Override
        public void onFailure(Throwable t) {
            log.info("@@ failed because of :: {}", t);
        }
    });

นอกจากนี้ยังมีข้อได้เปรียบที่คุณสามารถรวบรวมผลลัพธ์ทั้งหมดในที่เดียวเมื่อการประมวลผลเสร็จสิ้น ...

ข้อมูลเพิ่มเติมที่นี่


2
สะอาดมาก. ทำงานได้อย่างไม่มีที่ติแม้บน Android เพิ่งมีการใช้ในrunOnUiThread() onSuccess()
DSchmidt

12

CyclicBarrierชั้นใน Java 5 และต่อมาได้รับการออกแบบสำหรับการจัดเรียงของสิ่งนี้


7
เยี่ยมเลยจำชื่อโครงสร้างข้อมูลนี้ไม่ได้ อย่างไรก็ตามจะเหมาะสมก็ต่อเมื่อคุณทราบจำนวนงานที่จะเข้าคิวก่อนล่วงหน้า
ᆼᆺᆼ

ใช่คุณคิดว่าคุณจะสามารถตีสิ่งกีดขวางด้วยเธรดปัจจุบันและเธรดลูกทั้งหมดจากนั้นเมื่อคุณผ่านมันคุณจะรู้ว่าเธรดเด็กเสร็จแล้ว ...
rogerdpack

จริงๆแล้วมันเป็นคำตอบที่ผิด CyclicBarrier ออกแบบมาสำหรับบางส่วน CountDownLatch ออกแบบมาสำหรับการรอกิจกรรม
gstackoverflow

7

ทำตามวิธีใดวิธีหนึ่งด้านล่าง

  1. วนซ้ำทุกภารกิจในอนาคตที่ส่งคืนจากsubmitเปิดExecutorServiceและตรวจสอบสถานะด้วยการบล็อกการโทรget()บนFutureวัตถุตามที่แนะนำKiran
  2. ใช้ invokeAll()กับExecutorService
  3. CountDownLatch
  4. ForkJoinPoolหรือExecutors.html # newWorkStealingPool
  5. ใช้ shutdown, awaitTermination, shutdownNow APIs ของ ThreadPoolExecutor ในลำดับที่ถูกต้อง

คำถาม SE ที่เกี่ยวข้อง:

CountDownLatch ใช้ใน Java Multithreading อย่างไร

วิธีการปิดระบบ Java ExecutorService อย่างถูกต้อง


6

นี่คือสองตัวเลือกเพียงเล็กน้อยสับสนที่ดีที่สุดที่จะไป

ตัวเลือกที่ 1:

ExecutorService es = Executors.newFixedThreadPool(4);
List<Runnable> tasks = getTasks();
CompletableFuture<?>[] futures = tasks.stream()
                               .map(task -> CompletableFuture.runAsync(task, es))
                               .toArray(CompletableFuture[]::new);
CompletableFuture.allOf(futures).join();    
es.shutdown();

ตัวเลือก 2:

ExecutorService es = Executors.newFixedThreadPool(4);
List< Future<?>> futures = new ArrayList<>();
for(Runnable task : taskList) {
    futures.add(es.submit(task));
}

for(Future<?> future : futures) {
    try {
        future.get();
    }catch(Exception e){
        // do logging and nothing else
    }
}
es.shutdown();

ที่นี่วางในอนาคตรับ (); ในลองจับเป็นความคิดที่ดีใช่มั้ย


5

คุณสามารถห่องานของคุณใน runnable อื่นที่จะส่งการแจ้งเตือน:

taskExecutor.execute(new Runnable() {
  public void run() {
    taskStartedNotification();
    new MyTask().run();
    taskFinishedNotification();
  }
});

1
พาฉันสักครู่เพื่อดูว่าจะแก้ปัญหาคำถามของ OP ได้อย่างไร ก่อนอื่นให้สังเกตว่าการตัดนี้เป็นของแต่ละงานไม่ใช่ของรหัสที่เริ่มต้นงานทั้งหมด สันนิษฐานได้ว่าการเริ่มต้นแต่ละครั้งจะเพิ่มตัวนับและการสิ้นสุดแต่ละครั้งจะลดจำนวนตัวนับนั้นหรือจะเพิ่มขึ้นcompletedนับ ดังนั้นหลังจากเริ่มต้นพวกเขาทั้งหมดในแต่ละการแจ้งเตือนสามารถกำหนดได้ว่างานทั้งหมดเสร็จสมบูรณ์หรือไม่ โปรดทราบว่ามันเป็นสิ่งสำคัญที่จะใช้try/finallyเพื่อให้การแจ้งเตือนที่เสร็จสมบูรณ์ (หรือการแจ้งเตือนทางเลือกในcatchบล็อก) ได้รับแม้ว่างานจะล้มเหลว มิฉะนั้นจะรอตลอดไป
ToolmakerSteve

3

ฉันเพิ่งเขียนโปรแกรมตัวอย่างที่ช่วยแก้ปัญหาของคุณ ไม่มีการนำไปปฏิบัติที่รัดกุมดังนั้นฉันจะเพิ่มอีกหนึ่งรายการ แม้ว่าคุณจะสามารถใช้งานได้executor.shutdown()และexecutor.awaitTermination()มันก็ไม่ใช่วิธีปฏิบัติที่ดีที่สุดเนื่องจากเวลาที่ใช้โดยเธรดต่างๆจะไม่แน่นอน

ExecutorService es = Executors.newCachedThreadPool();
    List<Callable<Integer>> tasks = new ArrayList<>();

    for (int j = 1; j <= 10; j++) {
        tasks.add(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {
                int sum = 0;
                System.out.println("Starting Thread "
                        + Thread.currentThread().getId());

                for (int i = 0; i < 1000000; i++) {
                    sum += i;
                }

                System.out.println("Stopping Thread "
                        + Thread.currentThread().getId());
                return sum;
            }

        });
    }

    try {
        List<Future<Integer>> futures = es.invokeAll(tasks);
        int flag = 0;

        for (Future<Integer> f : futures) {
            Integer res = f.get();
            System.out.println("Sum: " + res);
            if (!f.isDone()) 
                flag = 1;
        }

        if (flag == 0)
            System.out.println("SUCCESS");
        else
            System.out.println("FAILED");

    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }

มันเป็นการดีที่คุณแสดงให้เห็นถึงการใช้งานในอนาคตรับ - ทางเลือกที่ดีที่จะรู้ แต่ทำไมคุณถึงคิดว่าดีกว่าที่จะรอตลอดไปกว่าตั้งค่าการหมดเวลาที่ยอมรับได้สูงสุด ที่สำคัญไม่มีเหตุผลที่จะทำตามตรรกะนี้ทั้งหมดเมื่อใครสามารถให้เวลาจริง ๆ และรอการประกาศถ้าคุณต้องการรอ (เป็นหลักตลอดไป) จนกว่างานทั้งหมดจะเสร็จสมบูรณ์
ToolmakerSteve

สิ่งนี้ไม่แตกต่างจากโซลูชันที่นำเสนอแล้วที่นี่ วิธีแก้ปัญหาของคุณเหมือนกันกับการนำเสนอโดย @sjlee
pulp_fiction

ไม่แน่ใจว่าทำไมคุณต้องตรวจสอบว่าเสร็จสิ้นเมื่อใดตาม oracle doc, invokeAll จะส่งคืนเฉพาะ "เมื่อหมดเวลาหรือหมดเวลาแล้วแต่จำนวนใดจะเกิดขึ้นก่อน"
Mashrur

3

เพียงเพื่อให้ทางเลือกเพิ่มเติมที่นี่แตกต่างกันเพื่อใช้สลัก / สิ่งกีดขวาง นอกจากนี้คุณยังจะได้รับผลลัพธ์บางส่วนจนกว่าทั้งหมดของพวกเขาเสร็จสิ้นการใช้CompletionService

จาก Java Concurrency ในทางปฏิบัติ: "ถ้าคุณมีชุดการคำนวณเพื่อส่งไปยังผู้บริหารและคุณต้องการดึงผลลัพธ์เมื่อพร้อมใช้งานคุณสามารถรักษาอนาคตที่เกี่ยวข้องกับแต่ละงานและโพลซ้ำ ๆ เพื่อให้เสร็จโดยโทรหา หมดเวลาของศูนย์นี้เป็นไปได้ แต่น่าเบื่อโชคดีที่มีวิธีที่ดีกว่า : บริการเสร็จ "

ที่นี่การดำเนินการ

public class TaskSubmiter {
    private final ExecutorService executor;
    TaskSubmiter(ExecutorService executor) { this.executor = executor; }
    void doSomethingLarge(AnySourceClass source) {
        final List<InterestedResult> info = doPartialAsyncProcess(source);
        CompletionService<PartialResult> completionService = new ExecutorCompletionService<PartialResult>(executor);
        for (final InterestedResult interestedResultItem : info)
            completionService.submit(new Callable<PartialResult>() {
                public PartialResult call() {
                    return InterestedResult.doAnOperationToGetPartialResult();
                }
        });

    try {
        for (int t = 0, n = info.size(); t < n; t++) {
            Future<PartialResult> f = completionService.take();
            PartialResult PartialResult = f.get();
            processThisSegment(PartialResult);
            }
        } 
        catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } 
        catch (ExecutionException e) {
            throw somethinghrowable(e.getCause());
        }
    }
}

3

นี่คือทางออกของฉันซึ่งใช้คำแนะนำ "AdamSkywalker" และใช้งานได้

package frss.main;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestHilos {

    void procesar() {
        ExecutorService es = Executors.newFixedThreadPool(4);
        List<Runnable> tasks = getTasks();
        CompletableFuture<?>[] futures = tasks.stream().map(task -> CompletableFuture.runAsync(task, es)).toArray(CompletableFuture[]::new);
        CompletableFuture.allOf(futures).join();
        es.shutdown();

        System.out.println("FIN DEL PROCESO DE HILOS");
    }

    private List<Runnable> getTasks() {
        List<Runnable> tasks = new ArrayList<Runnable>();

        Hilo01 task1 = new Hilo01();
        tasks.add(task1);

        Hilo02 task2 = new Hilo02();
        tasks.add(task2);
        return tasks;
    }

    private class Hilo01 extends Thread {

        @Override
        public void run() {
            System.out.println("HILO 1");
        }

    }

    private class Hilo02 extends Thread {

        @Override
        public void run() {
            try {
                sleep(2000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("HILO 2");
        }

    }


    public static void main(String[] args) {
        TestHilos test = new TestHilos();
        test.procesar();
    }
}

2

คุณสามารถใช้รหัสนี้:

public class MyTask implements Runnable {

    private CountDownLatch countDownLatch;

    public MyTask(CountDownLatch countDownLatch {
         this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
         try {
             //Do somethings
             //
             this.countDownLatch.countDown();//important
         } catch (InterruptedException ex) {
              Thread.currentThread().interrupt();
         }
     }
}

CountDownLatch countDownLatch = new CountDownLatch(NUMBER_OF_TASKS);
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
for (int i = 0; i < NUMBER_OF_TASKS; i++){
     taskExecutor.execute(new MyTask(countDownLatch));
}
countDownLatch.await();
System.out.println("Finish tasks");

2

ฉันสร้างตัวอย่างการทำงานต่อไปนี้ แนวคิดคือมีวิธีการประมวลผลกลุ่มของงาน (ฉันใช้คิวเป็นตัวอย่าง) กับเธรดจำนวนมาก (กำหนดโดยทางโปรแกรมโดย numberOfTasks / threshold) และรอจนกว่าเธรดทั้งหมดจะเสร็จสมบูรณ์เพื่อดำเนินการประมวลผลอื่นต่อไป

import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/** Testing CountDownLatch and ExecutorService to manage scenario where
 * multiple Threads work together to complete tasks from a single
 * resource provider, so the processing can be faster. */
public class ThreadCountDown {

private CountDownLatch threadsCountdown = null;
private static Queue<Integer> tasks = new PriorityQueue<>();

public static void main(String[] args) {
    // Create a queue with "Tasks"
    int numberOfTasks = 2000;
    while(numberOfTasks-- > 0) {
        tasks.add(numberOfTasks);
    }

    // Initiate Processing of Tasks
    ThreadCountDown main = new ThreadCountDown();
    main.process(tasks);
}

/* Receiving the Tasks to process, and creating multiple Threads
* to process in parallel. */
private void process(Queue<Integer> tasks) {
    int numberOfThreads = getNumberOfThreadsRequired(tasks.size());
    threadsCountdown = new CountDownLatch(numberOfThreads);
    ExecutorService threadExecutor = Executors.newFixedThreadPool(numberOfThreads);

    //Initialize each Thread
    while(numberOfThreads-- > 0) {
        System.out.println("Initializing Thread: "+numberOfThreads);
        threadExecutor.execute(new MyThread("Thread "+numberOfThreads));
    }

    try {
        //Shutdown the Executor, so it cannot receive more Threads.
        threadExecutor.shutdown();
        threadsCountdown.await();
        System.out.println("ALL THREADS COMPLETED!");
        //continue With Some Other Process Here
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
}

/* Determine the number of Threads to create */
private int getNumberOfThreadsRequired(int size) {
    int threshold = 100;
    int threads = size / threshold;
    if( size > (threads*threshold) ){
        threads++;
    }
    return threads;
}

/* Task Provider. All Threads will get their task from here */
private synchronized static Integer getTask(){
    return tasks.poll();
}

/* The Threads will get Tasks and process them, while still available.
* When no more tasks available, the thread will complete and reduce the threadsCountdown */
private class MyThread implements Runnable {

    private String threadName;

    protected MyThread(String threadName) {
        super();
        this.threadName = threadName;
    }

    @Override
    public void run() {
        Integer task;
        try{
            //Check in the Task pool if anything pending to process
            while( (task = getTask()) != null ){
                processTask(task);
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            /*Reduce count when no more tasks to process. Eventually all
            Threads will end-up here, reducing the count to 0, allowing
            the flow to continue after threadsCountdown.await(); */
            threadsCountdown.countDown();
        }
    }

    private void processTask(Integer task){
        try{
            System.out.println(this.threadName+" is Working on Task: "+ task);
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
}
}

หวังว่ามันจะช่วย!


1

คุณสามารถใช้คลาสย่อยของExecutorCompletionServiceเพื่อห่อtaskExecutorและใช้BlockingQueueของคุณเองเพื่อรับทราบเมื่องานแต่ละงานเสร็จสมบูรณ์และดำเนินการติดต่อกลับหรือการดำเนินการใด ๆ ที่คุณต้องการเมื่อจำนวนงานที่เสร็จสมบูรณ์ไปถึงเป้าหมายที่คุณต้องการ


1

คุณควรใช้executorService.shutdown()และexecutorService.awaitTerminationวิธีการ

ตัวอย่างดังต่อไปนี้:

public class ScheduledThreadPoolExample {

    public static void main(String[] args) throws InterruptedException {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        executorService.scheduleAtFixedRate(() -> System.out.println("process task."),
                0, 1, TimeUnit.SECONDS);

        TimeUnit.SECONDS.sleep(10);
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.DAYS);
    }

}

กำลังรอคอยการประกาศ () จำเป็นหลังจากปิด () /
gaurav

1

ดังนั้นฉันโพสต์คำตอบของฉันจากคำถามที่เชื่อมโยงที่นี่กรณีมีคนต้องการวิธีที่ง่ายกว่านี้

ExecutorService executor = Executors.newFixedThreadPool(10);
CompletableFuture[] futures = new CompletableFuture[10];
int i = 0;
while (...) {
    futures[i++] =  CompletableFuture.runAsync(runner, executor);
}

CompletableFuture.allOf(futures).join(); // THis will wait until all future ready.

0

Java 8 - เราสามารถใช้ stream API เพื่อประมวลผลสตรีม โปรดดูตัวอย่างด้านล่าง

final List<Runnable> tasks = ...; //or any other functional interface
tasks.stream().parallel().forEach(Runnable::run) // Uses default pool

//alternatively to specify parallelism 
new ForkJoinPool(15).submit(
          () -> tasks.stream().parallel().forEach(Runnable::run) 
    ).get();

2
สวัสดีวลาดยินดีต้อนรับสู่ StackOverflow คุณช่วยแก้ไขคำตอบของคุณเพื่ออธิบายว่าสิ่งนี้ตอบคำถามได้อย่างไรและรหัสทำงานอย่างไร ไม่มีคำตอบเฉพาะรหัสเท่านั้น ขอบคุณ!
ทิมมาโลน

โพสต์นี้พูดถึงเกี่ยวกับการเห็นพ้องด้วย Parallelism! = Concurrency
GabrielBB

0

ExecutorService WORKER_THREAD_POOL 
  = Executors.newFixedThreadPool(10);
CountDownLatch latch = new CountDownLatch(2);
for (int i = 0; i < 2; i++) {
    WORKER_THREAD_POOL.submit(() -> {
        try {
            // doSomething();
            latch.countDown();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    });
}

// wait for the latch to be decremented by the two remaining threads
latch.await();

หากdoSomething()มีข้อยกเว้นอื่น ๆlatch.countDown()ดูเหมือนว่าจะไม่ดำเนินการดังนั้นฉันควรทำอย่างไร


0

ถ้าคุณใช้เธรด ExecutionServices เพิ่มเติม SEQUENTIALLY และต้องการรอ EEC EXECUTIONSERVICE ให้เสร็จสิ้น วิธีที่ดีที่สุดคือเหมือนด้านล่าง;

ExecutorService executer1 = Executors.newFixedThreadPool(THREAD_SIZE1);
for (<loop>) {
   executer1.execute(new Runnable() {
            @Override
            public void run() {
                ...
            }
        });
} 
executer1.shutdown();

try{
   executer1.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);

   ExecutorService executer2 = Executors.newFixedThreadPool(THREAD_SIZE2);
   for (true) {
      executer2.execute(new Runnable() {
            @Override
            public void run() {
                 ...
            }
        });
   } 
   executer2.shutdown();
} catch (Exception e){
 ...
}

-1

สิ่งนี้อาจช่วยได้

Log.i(LOG_TAG, "shutting down executor...");
executor.shutdown();
while (true) {
                try {
                    Log.i(LOG_TAG, "Waiting for executor to terminate...");
                    if (executor.isTerminated())
                        break;
                    if (executor.awaitTermination(5000, TimeUnit.MILLISECONDS)) {
                        break;
                    }
                } catch (InterruptedException ignored) {}
            }

-1

คุณสามารถโทรwaitTillDone ()ในระดับRunnerนี้:

Runner runner = Runner.runner(4); // create pool with 4 threads in thread pool

while(...) {
    runner.run(new MyTask()); // here you submit your task
}


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


runner.shutdown(); // once you done you can shutdown the runner

คุณสามารถนำคลาสนี้กลับมาใช้ใหม่และโทร waitTillDone () หลาย ๆ ครั้งตามที่คุณต้องการก่อนที่จะปิดการโทร () รวมถึงรหัสของคุณนั้นง่ายมาก นอกจากนี้คุณไม่จำเป็นต้องรู้จำนวนของงานล่วงหน้า

หากต้องการใช้งานเพียงเพิ่ม gradle / maven นี้ compile 'com.github.matejtymes:javafixes:1.3.1'นี้ในโครงการของคุณ

รายละเอียดเพิ่มเติมสามารถดูได้ที่นี่:

https://github.com/MatejTymes/JavaFixes


-2

มีวิธีการในตัวจัดการgetActiveCount()- ที่ให้จำนวนเธรดที่ใช้งานอยู่

หลังจากการขยายเธรดเราสามารถตรวจสอบว่ามีactiveCount()ค่า0หรือไม่ เมื่อค่าเป็นศูนย์หมายความว่าไม่มีเธรดที่ใช้งานอยู่ซึ่งทำงานอยู่ซึ่งหมายความว่างานจะเสร็จสิ้น:

while (true) {
    if (executor.getActiveCount() == 0) {
    //ur own piece of code
    break;
    }
}

3
ไม่ใช่ความคิดที่ดีโปรดดูstackoverflow.com/a/7271685/1166992และ javadoc: "ส่งคืนจำนวนเธรดโดยประมาณที่กำลังเรียกใช้งานอย่างแข็งขัน"
Olivier Faucheux
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.