Home > Blockchain >  I want to stop a group of completed threads, wait for uncompleted threads
I want to stop a group of completed threads, wait for uncompleted threads

Time:09-23

I need a group of threads to run at the same time and then another group of threads after that. for example, 10 threads start working, and then 10 or 15 other threads. of course the first approach I tried was to make loop.

while (true) {
        for (int i = 0; i < 10; i  ) {
            Thread thread = new Thread(
                    new Runnable() {
                        @Override
                        public void run() {
                            System.out.println("hi");
                        }
                    });
            thread.start();
        }
    }

but the problem is when scenario like this happens: imagine if in first iteration, 8 threads finished their tasks, and 2 threads take longer time. the next 10 threads wont start until all 8 2 (completed and not completed) threads finish. while I want an approach where 8 threads get replaced by 8 of waiting to start threads.

CodePudding user response:

For adding tasks to threads and replacing them you can use ExecutorService. You can create it by using:

ExecutorService executor = Executors.newFixedThreadPool(10);

CodePudding user response:

Instead of managing your Threads manually, it definitely would be wise to look at the facilities provided by the implementations of the ExecutorService interfaces.

Things would be a bit earthier if you use Callable interface for your task instead of Runnable. Callable is more handy in many cases because it allows obtaining the result from the worker-thread and also propagating an exception if thing went wrong (as opposed run() would force you to catch every checked exception). If you have in mind something more interesting than printing a dummy message, you might find Callable to be useful for your purpose.

ExecutorService.invokeAll() Callable

ExecutorService has a blocking method invokeAll() which expects a collection of the callable-tasks and return a list of completed Future objects when all the tasks are done.

To generate a light-weight collection of repeated elements (since we need to fire a bunch of identical tasks) we can use utility method Collections.nCopies().

Here's a sample code which repeatedly runs a dummy task:

ExecutorService executor = Executors.newWorkStealingPool();

while (true) {
    executor.invokeAll(Collections.nCopies(10, () -> {
        System.out.println("hi");
        return true;
    }));
}

To make sure that it does what expected, we can add a counter of iterations and display it on the console and Thread.currentThread().sleep() to avoid cluttering the output very fast (for the same reason, the number of tasks reduced to 3):

public static void main(String[] args) throws InterruptedException {
    ExecutorService executor = Executors.newWorkStealingPool();

    int counter = 0;
    
    while (true) {
        System.out.println("iteration: "   counter  );
        
        executor.invokeAll(Collections.nCopies(3, () -> {
            System.out.println("hi");
            return true;
        }));
        
        Thread.currentThread().sleep(1000);
    }
}

Output:

iteration: 0
hi
hi
hi
iteration: 1
hi
hi
hi
... etc.

CompletableFuture.allOf().join() Runnable

Another possibility is to use CompletableFuture API, and it's method allOf() which expects a varargs of submitted tasks in the form CompletableFuture and return a single CompletableFuture which would be completed when all provided arguments are done.

In order to synchronize the execution of the tasks with the main thread, we need to invoke join() on the resulting CompletableFuture instance.

That's how it might be implemented:

public static void main(String[] args) throws InterruptedException {
    ExecutorService executor = Executors.newWorkStealingPool();
    Runnable task = () -> System.out.println("hi");

    int counter = 0;
    
    while (true) {
        System.out.println("iteration: "   counter  );

        CompletableFuture.allOf(
            Stream.generate(() -> task)
                .limit(3)
                .map(t -> CompletableFuture.runAsync(t, executor))
                .toArray(CompletableFuture<?>[]::new)
        ).join();
        
        Thread.currentThread().sleep(1000);
    }
}

Output:

iteration: 0
hi
hi
hi
iteration: 1
hi
hi
hi
... etc.

ScheduledExecutorService

I suspect you might interested in scheduling these tasks instead of running them reputedly. If that's the case, have a look at ScheduledExecutorService and it's methods scheduleAtFixedRate() and scheduleWithFixedDelay().

  • Related