Search code examples
javamultithreadingschedulingfuturetask

Java ScheduledFuture get List


This code always returns me 10. I think that problem with receiving list of all features. I need to parse every feature and stop execution scheduler when variable limit will equals 5. How can I do this?

static int limit = 0;
static final int testNum = 10;

static ScheduledExecutorService scheduler;
public static void main(String[] args) {
    scheduler = Executors
            .newScheduledThreadPool(5);
    ScheduledFuture<Integer> future = scheduler.schedule(new ScheduledPrinter(), 10, TimeUnit.SECONDS);
    try {
        while (true) {
            System.out.println(future.get());
            if(future.get() != testNum){
                return;
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}
private static class ScheduledPrinter implements Callable<Integer> {
    public Integer call() throws Exception {
        limit++;
        if(limit==5) {
            scheduler.shutdown();
            return limit;
        }
        return testNum;
    }
}

Solution

  • Let's see What's happening here. scheduler.schedule(new ScheduledPrinter(), 10, TimeUnit.SECONDS) runs the ScheduledPrinter.call() only once. Here is the API docs.

    What you want is probably a scheduleAtFixedRate. This takes a Runnable instead of a callable, so the code will look something like this:

    static volatile int limit = 0; // make it volatile because of *possible* multithreaded access
                                   // an AtomicInteger would do too
    static final int testNum = 10;
    
    static ScheduledExecutorService scheduler;
    
    public static void main(String[] args) {
        scheduler = Executors
                .newScheduledThreadPool(5);
        // discarding the future. No need to use it here.
        ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(new ScheduledPrinter(), 10L, 10L, TimeUnit.SECONDS);
    }
    
    /** Printing and counting happens here **/
    private static class ScheduledPrinter implements Runnable {
    
        @Override
        public void run() {
            limit++;
            if(limit==5) {
                scheduler.shutdown();
                printNum(limit);
            } else {
                printNum(testNum);
            }
        }
    
        private void printNum(int num) {
            System.out.println(num);
        }
    }
    

    Update

    OP asked how to return values from Runnable.run() method? Unfortunately, it's impossible. We have to choose between periodical run and a return value because ScheduledExecutorService cannot do both.

    It's still possible to get a value out of the Runnable. We must share a reference for this. Here is a rudimentary approach:

        final Queue<Integer> numsPrinted = new ConcurrentLinkedQueue<>(); // a concurrent collection
        ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay( // using scheduleWithFixedDelay because probably this is what you want
                new ScheduledPrinter(numsPrinted), // passing the reference
                10L, 10L, TimeUnit.SECONDS);
        try {
            future.isDone();
            Object obj = future.get(80, TimeUnit.SECONDS); // blocks until 80 secs or until the task is done
            System.out.println(obj);
            System.out.println(Arrays.toString(numsPrinted.toArray()));
        } catch (TimeoutException e) {
            System.out.println(Arrays.toString(numsPrinted.toArray()));
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } 
    

    The ScheduledPrinter now looks like this:

    private static class ScheduledPrinter implements Runnable {
    
        private final Queue<Integer> numsPrinted;
    
        public ScheduledPrinter(Queue<Integer> numsPrinted) {
            this.numsPrinted = numsPrinted; // storing the reference
        }
    
        @Override
        public void run() {
            limit++;
            if(limit==5) {
                //scheduler.awaitTermination(timeout, unit)
                scheduler.shutdown();
                storeAndPrintNum(limit);
            } else {
                storeAndPrintNum(testNum);
            }
        }
    
        private void storeAndPrintNum(int num) {
            numsPrinted.add(num); // using the reference
            System.out.println(num);
        }
    }