How to use invokeAll() to let all thread pool do their task?

前端 未结 3 1265
失恋的感觉
失恋的感觉 2020-11-29 03:15
    ExecutorService pool=Executors.newFixedThreadPool(7);
        List> future=new ArrayList>();
        List<         


        
相关标签:
3条回答
  • 2020-11-29 03:23

    invokeAll is a blocking method. It means – JVM won’t proceed to next line until all the threads are completed. So I think there is something wrong with your thread future result.

    System.out.println("name is:"+future.get(i).get().getName());
    

    from this line I think there are some futures have no result and can be null, So you should check your code, if there are some Futures null,If so, get a if before this line executed.

    0 讨论(0)
  • 2020-11-29 03:35

    Future.get() throws below exceptions.

    CancellationException - if the computation was cancelled

    ExecutionException - if the computation threw an exception

    InterruptedException - if the current thread was interrupted while waiting

    Catch all these exceptions when you call get() method.

    I have simulated divide by zero exception for some Callable tasks but exception in one Callable does not affect other Callable tasks submitted to ExecutorService if you catch above three exceptions as shown in example code.

    Example code snippet:

    import java.util.concurrent.*;
    import java.util.*;
    
    public class InvokeAllUsage{
        public InvokeAllUsage(){
            System.out.println("creating service");
            ExecutorService service = Executors.newFixedThreadPool(10);
    
            List<MyCallable> futureList = new ArrayList<MyCallable>();
            for ( int i=0; i<10; i++){
                MyCallable myCallable = new MyCallable((long)i+1);
                futureList.add(myCallable);
            }
            System.out.println("Start");
            try{
                List<Future<Long>> futures = service.invokeAll(futureList);  
                for(Future<Long> future : futures){
                    try{
                        System.out.println("future.isDone = " + future.isDone());
                        System.out.println("future: call ="+future.get());
                    }
                    catch (CancellationException ce) {
                        ce.printStackTrace();
                    } catch (ExecutionException ee) {
                        ee.printStackTrace();
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt(); // ignore/reset
                    }
                }
            }catch(Exception err){
                err.printStackTrace();
            }
            System.out.println("Completed");
            service.shutdown();
        }
        public static void main(String args[]){
            InvokeAllUsage demo = new InvokeAllUsage();
        }
        class MyCallable implements Callable<Long>{
            Long id = 0L;
            public MyCallable(Long val){
                this.id = val;
            }
            public Long call(){
    
                if ( id % 5 == 0){
                    id = id / 0;
                }           
                return id;
            }
        }
    }
    

    output:

    creating service
    Start
    future.isDone = true
    future: call =1
    future.isDone = true
    future: call =2
    future.isDone = true
    future: call =3
    future.isDone = true
    future: call =4
    future.isDone = true
    java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
            at java.util.concurrent.FutureTask.report(FutureTask.java:122)
            at java.util.concurrent.FutureTask.get(FutureTask.java:188)
            at InvokeAllUsage.<init>(InvokeAllUsage.java:20)
            at InvokeAllUsage.main(InvokeAllUsage.java:37)
    Caused by: java.lang.ArithmeticException: / by zero
            at InvokeAllUsage$MyCallable.call(InvokeAllUsage.java:47)
            at InvokeAllUsage$MyCallable.call(InvokeAllUsage.java:39)
            at java.util.concurrent.FutureTask.run(FutureTask.java:262)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
            at java.lang.Thread.run(Thread.java:744)
    future.isDone = true
    future: call =6
    future.isDone = true
    future: call =7
    future.isDone = true
    future: call =8
    future.isDone = true
    future: call =9
    future.isDone = true
    java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
            at java.util.concurrent.FutureTask.report(FutureTask.java:122)
            at java.util.concurrent.FutureTask.get(FutureTask.java:188)
            at InvokeAllUsage.<init>(InvokeAllUsage.java:20)
            at InvokeAllUsage.main(InvokeAllUsage.java:37)
    Caused by: java.lang.ArithmeticException: / by zero
            at InvokeAllUsage$MyCallable.call(InvokeAllUsage.java:47)
            at InvokeAllUsage$MyCallable.call(InvokeAllUsage.java:39)
            at java.util.concurrent.FutureTask.run(FutureTask.java:262)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
            at java.lang.Thread.run(Thread.java:744)
    Completed
    
    0 讨论(0)
  • 2020-11-29 03:45

    The way an ExecutorService works is that when you call invokeAll it waits for all tasks to complete:

    Executes the given tasks, returning a list of Futures holding their status and results when all complete. Future.isDone() is true for each element of the returned list. Note that a completed task could have terminated either normally or by throwing an exception. The results of this method are undefined if the given collection is modified while this operation is in progress.1(emphasis added)

    What that means is that your tasks are all done but some may have thrown an exception. This exception is part of the Future - calling get causes the exception to be rethrown wrapped in an ExecutionException.

    From you stacktrack

    java.util.concurrent.ExecutionException: java.lang.NullPointerException at
    java.util.concurrent.FutureTask$Sync.innerGet(Unknown Source) at
    java.util.concurrent.FutureTask.get(Unknown Source) at 
                                    ^^^ <-- from get
    

    You can see that this is indeed the case. One of your tasks has failed with an NPE. The ExecutorService caught the exception and is telling you about it by throwing an ExecutionException when you call Future.get.

    Now, if you want to take tasks as they complete you need an ExecutorCompletionService. This acts as a BlockingQueue that will allow you to poll for tasks as and when they finish.

    public static void main(String[] args) throws Exception {
        final ExecutorService executorService = Executors.newFixedThreadPool(10);
        final ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executorService);
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; ++i) {
                    try {
                        final Future<String> myValue = completionService.take();
                        //do stuff with the Future
                        final String result = myValue.get();
                        System.out.println(result);
                    } catch (InterruptedException ex) {
                        return;
                    } catch (ExecutionException ex) {
                        System.err.println("TASK FAILED");
                    }
                }
            }
        });
        for (int i = 0; i < 100; ++i) {
            completionService.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    if (Math.random() > 0.5) {
                        throw new RuntimeException("FAILED");
                    }
                    return "SUCCESS";
                }
            });
        }
        executorService.shutdown();
    }
    

    In this example I have one task that calls take on the ExecutorCompletionService which gets the Futures as they become available and then I submit tasks to the ExecutorCompletionService.

    This will allow you to get the failed task as soon as it fails rather than having to wait for all the tasks to either fail of succeed together.

    The only complication is that it is difficult to tell the polling thread that all the tasks are done as everything is now asynchronous. In this instance I have used the knowledge that 100 tasks will be submitted so that it only need to poll 100 times. A more general way would be to collect the Futures from the submit method as well and then loop over them to see if everything is completed.

    0 讨论(0)
提交回复
热议问题