How can I asynchronously execute 20 Runnable tasks(or 1 task 20 times), using 5 CompletableFutures?
That's what I've got:
Runnable task = () -> {
long startTime = System.currentTimeMillis();
Random random = new Random();
while (System.currentTimeMillis() - startTime < 3000) {
DoubleStream.generate(() -> random.nextDouble())
.limit(random.nextInt(100))
.map(n -> Math.cos(n))
.sum();
}
System.out.println("Done");
};
for (int i = 0; i < 4; i++) {
CompletableFuture<Void> future1 = CompletableFuture.runAsync(task);
CompletableFuture<Void> future2 = CompletableFuture.runAsync(task);
CompletableFuture<Void> future3 = CompletableFuture.runAsync(task);
CompletableFuture<Void> future4 = CompletableFuture.runAsync(task);
CompletableFuture<Void> future5 = CompletableFuture.runAsync(task);
future1.get();
future2.get();
future3.get();
future4.get();
future5.get();
}
If I execute this code, I can see that it only runs 3 future.get() asynchronously:
3 and then 2 that's left during 1 for() iteration
So, I would like to do all 20 tasks, as asynchronously as possible
You can use allOf to run several tasks simultaneously as one. First I create a combined of 5 tasks (the same as in your question) but then I added 10 instead (and only loped twice) and got half the execution time.
for (int i = 0; i < 2; i++) {
CompletableFuture<Void> future1 = CompletableFuture.runAsync(task);
CompletableFuture<Void> future2 = CompletableFuture.runAsync(task);
// and so on until ten
CompletableFuture<Void> future10 = CompletableFuture.runAsync(task);
CompletableFuture<Void> combined = CompletableFuture.allOf(future1, future2, future3, future4, future5, future6, future7, future8, future9, future10);
combined.get();
}
The default executor of CompletableFuture is the common pool of the ForkJoinPool, which has a default target parallelism matching the number of CPU cores minus one. So if you have four cores, at most three jobs will get executed asynchronously. Since you are forcing a wait for completion every 5 jobs, you’ll get three parallel executions, followed by two parallel executions in every loop iteration.
If you want to get a particular execution strategy like parallelism of your choice, the best way is to specify a properly configured executor. Then, you should let the executor manage the parallelism instead of waiting in a loop.
ExecutorService pool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 20; i++) {
CompletableFuture.runAsync(task, pool);
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.DAYS); // wait for the completion of all tasks
This allows five parallel jobs, but will let each of the five threads pick up a new job immediately after one completed, instead of waiting for the next loop iteration.
But when you say
So, I would like to do all 20 tasks, as asynchronously as possible
it’s not clear why you are enforcing a wait after scheduling five jobs at all. The maximum parallelism can be achieve via
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
CompletableFuture.runAsync(task, pool);
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.DAYS); // wait for the completion of all tasks
This may spawn as many threads as jobs, unless one job completes before all have been scheduled, as in this case the worker thread may pick up a new job.
But this logic doesn’t require a CompletableFuture at all. You can also use:
ExecutorService pool = Executors.newCachedThreadPool();
// schedule 20 jobs and return when all completed
pool.invokeAll(Collections.nCopies(20, Executors.callable(task)));
pool.shutdown();
But when your job does not involve I/O or any other kind of waiting resp. releasing the CPU, there is no point in creating more threads than CPU cores. A pool configured to the number of processors is preferable.
ExecutorService pool = Executors.newWorkStealingPool(
Runtime.getRuntime().availableProcessors());
// schedule 20 jobs at return when all completed
pool.invokeAll(Collections.nCopies(20, Executors.callable(task)));
pool.shutdown();
In your special case this likely runs slower as your jobs use the system time to appear running faster when having more threads than cores, but are actually doing less work then. But for ordinary computational task, this will improve the performance.
Set the following system property to the number of threads you want the common fork join pool to use:
java.util.concurrent.ForkJoinPool.common.parallelism
See ForkJoinPool
The reason being that you do not specify your own fork join pool when constructing your completable futures, so it implicitly uses
ForkJoinPool.commonPool()
See CompletableFurure
Related
I have a task that I want to run 500 times in parallel, if I want to run it only once I would do something like this
Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(
taskProvider.get(),
0,
100,
TimeUnit.MILLISECONDS
);
I want to run 500 tasks(high IO) that I get from the task provider in parallel with fixed rate, is there a way to do this without creating 500 executors or submitting 500 tasks in a while loop?
EDIT: I know I can create a thread pool of size 500, if I do that I will have to submit 500 tasks(?) to run them in parallel, I was looking if there is a way to do it without submitting the similar tasks since basically they have the same content, I also don't want to start all of them at the same time, I want to start 50 every 100ms.
You would have to do this manually:
void submitTaskChunks(int chunkCount, int chunkSize, long delayMillis) throws InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < chunkCount; i++) {
List<Callable<Void>> taskChunk = IntStream.range(0, chunkSize)
.mapToObj(k -> taskProvider.get())
.collect(Collectors.toList());
executorService.invokeAll(taskChunk);
Thread.sleep(delayMillis);
}
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
}
To submit 50 tasks every 100 ms 10 times:
submitTaskChunks(10, 50, 100);
Note that taskProvider.get should return Callable<Void>.
You can create list of tasks of size 500 and use
ExecutorService executor = Executors.newFixedThreadPool(500);
executor.invokeAll(tasks);
I want to create a cached thread pool, but it acts as a fixed one. Currently I have this code:
public class BackgroundProcesses {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//ExecutorService threadPool2 = Executors.newCachedThreadPool();
ExecutorService threadPool = new ThreadPoolExecutor(2, 10, 180, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
for (int i = 0; i < 800; i++) {
Callable<String> task = new Task();
threadPool.submit(task);
}
}
}
class Task implements Callable<String> {
#Override
public String call() throws Exception {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + " is ready");
return "";
}
}
If I run the code I get output:
pool-1-thread-1 is ready
pool-1-thread-2 is ready
pool-1-thread-1 is ready
pool-1-thread-2 is ready
...
Meaning only 2 threads are doing all the work and no new worker threads are added to the pool. Shouldn't the threadpool spawn more threads if tasks are waiting in queue (up to 10 in my case)?
I don't want to use Executors.newCachedThreadPool() because it has practically no upper limit on maximum threads and it has corePoolSize 0. I want to have some threads ready at all times for better responsiveness.
----- edit 1 -----
Thank you Aleksey for the answer. Setting capacity to queue is making it behave as expected, but now I have a new problem.
The amount of background tasks vary a lot. Most of the time 0 but can go up to 50 concurrent tasks for short periods. What would be an efficient way to handle this? Keep in mind most background tasks would be short-lived (< 1s) but a few long lived tasks (> 1min) as well.
If I set my threadpool up like this:
ExecutorService threadPool = new ThreadPoolExecutor(2, 10, 180, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
I will most likely get RejectedExecutionException with peak usage. However if I set threadpool up like this:
ExecutorService threadPool = new ThreadPoolExecutor(2, 10, 180, TimeUnit.SECONDS, new LinkedBlockingQueue<>(200));
Then no new worker threads will ever be added because the queue won't max out.
The CPU has at least 4 cores so this would be wasteful in my opinion. And most of the time there isn't any background tasks at all (80% of up time), so keeping a fixed thread pool would also be wasteful in my opinion.
ThreadPoolExecutor Javadoc says:
When a new task is submitted in method execute(Runnable), and fewer
than corePoolSize threads are running, a new thread is created to
handle the request, even if other worker threads are idle. If there
are more than corePoolSize but less than maximumPoolSize threads
running, a new thread will be created only if the queue is full
Your LinkedBlockingQueue is never full, because it does not have an upper bound on the number of elements. Changing new LinkedBlockingQueue() to new LinkedBlockingQueue(10) would solve that.
I want to launch a lot of tasks to run on a database of +-42Mio records. I want to run this in batches of 5000 records/time (results in 850 tasks).
I also want to limit the number of threads (to 16) java starts to do this for me and I am using the current code to accomplish this task:
ExecutorService executorService = Executors.newFixedThreadPool(16);
for (int j = 1; j < 900 + 1; j++) {
int start = (j - 1) * 5000;
int stop = (j) * 5000- 1;
FetcherRunner runner = new FetcherRunner(routes, start, stop);
executorService.submit(runner);
Thread t = new Thread(runner);
threadsList.add(t);
t.start();
}
Is this the correct way to do this? Particularly as I have the impression that java just fires away all tasks ...(FetcherRunner implements runnable)
The first part using ExecutorService looks good:
...
FetcherRunner runner = new FetcherRunner(routes, start, stop);
executorService.submit(runner);
The part with Thread should not be there, I am assuming you have it there just to show how you had it before?
Update:
Yes, you don't require the code after executorService.submit(runner), that is going to end up spawning a huge number of threads. If your objective is to wait for all submitted tasks to complete after the loop, then you can get a reference to Future when submitting tasks and wait on the Future, something like this:
ExecutorService executorService = Executors.newFixedThreadPool(16);
List<Future<Result>> futures = ..;
for (int j = 1; j < 900+ 1; j++) {
int start = (j - 1) * 5000;
int stop = (j) * 5000- 1;
FetcherRunner runner = new FetcherRunner(routes, start, stop);
futures.add(executorService.submit(runner));
}
for (Future<Result> future:futures){
future.get(); //Do something with the results..
}
Is this the correct way of working?
The first part is correct. But you shouldn't be creating and starting new Thread objects. When you submit the Runnable, the ExecutorService puts it on its queue, and then runs it when a worker thread becomes available.
.... I use the threadlist to detect when all my threads are finished so I can continue processing results.
Well if you do what you are currently doing, you are running each task twice. Worse still, the swarm of manually created threads will all try to run in parallel.
A simple way to make sure that all of the tasks have completed is to call awaitTermination(...) on the ExecutorService. (An orderly shutdown of the executor service will have the same effect ... if you don't intend to use it again.)
The other approach is to create a Future for each FetcherRunner's results, and attempt to get the result after all of the tasks have been submitted. That has the advantage that you can start processing early results before later ones have been produced. (However, if you don't need to ... or can't ... do that, using Futures won't achieve anything.)
You don't need to the part after the call to submit. The code you have that creates a Thread will result in 900 threads being created! Yowza. The ExecutorService has a pool of 16 threads and you can run 16 jobs at once. Any jobs submitted when all 16 threads are busy will be queued. From the docs:
Creates a thread pool that reuses a fixed number of threads operating
off a shared
unbounded queue. At any point, at most nThreads threads will be active processing tasks.
If additional tasks are submitted when all threads are active, they will wait in the
queue until a thread is available. If any thread terminates due to a failure during
execution prior to shutdown, a new one will take its place if needed to execute
subsequent tasks. The threads in the pool will exist until it is explicitly shutdown.
So there is no need for yet another thread. If you need to be notified after a task has finished you can have it call out. Other options are to cache all of the Future's returned from submit, and upon each task being finished you can check to see if all Future's are done. After all Future's are finished you can dispatch another function to run. But it will run ON one of the threads in the ExecutorService.
Changed from your code:
ExecutorService executorService = Executors.newFixedThreadPool(16);
for (int j = 1; j < 900 + 1; j++) {
int start = (j - 1) * 5000;
int stop = (j) * 5000 - 1;
FetcherRunner runner = new FetcherRunner(routes, start, stop);
executorService.submit(runner);
}
The best way would be to use countdownlatch as follows
ExecutorService executorService = Executors.newFixedThreadPool(16);
CountdownLatch latch = new CountdownLatch(900);
FetcherRunner runner = new FetcherRunner(routes, start, stop, latch);
latch.await();
in the FetcherRunner under finally block use latch.countDown(); code after await() will be executed only when all the tasks are completed.
I am trying to understand the ExecutorService in java. There is not much performance difference when I use 1 thread or 4 threads. I have a quad core CPU and I do not have any other process running.
ExecutorService exService = Executors.newFixedThreadPool(4);
exService.execute(new Test().new RunnableThread());
exService.awaitTermination(25, TimeUnit.SECONDS);
class RunnableThread implements Runnable {
#Override
public void run() {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
long cnt = 0;
for (cnt = 0; cnt < 999999999; cnt++) {
try {
for (long j = 0; j < 20; j++){
x += j;
}
} catch (Exception e) {
e.printStackTrace();
}
}
stopWatch.stop();
System.out.println(stopWatch.getTime());
}
}
If my understanding is right, my task should have close to 4x performance improvement when I say newFixedThreadPool(4) right?
Unfortunately, there is no magic in the allocation of workload to threads.
Every task runs on its own thread. It does not somehow automatically get transformed into concurrent execution paths.
If you have only one task, the remaining three threads will be idle.
Multiple threads only speed up things if you can split your workload into multiple tasks that can run concurrently (and you have to do that splitting yourself).
If my understanding is right, my task should have close to 4x performance improvement when I say newFixedThreadPool(4) right?
Yes, if you're actually running 4 concurrent tasks.
Currently, you have a single task that you are submitting to the executor. Let's say that it takes 10 seconds. Even if you have 4 cores and 4 threads, Java will not be able to parallelize a single task. However, if you submit 4 independent tasks (that have no memory or lock contention), then you will see all of them complete in those 10 seconds that it took the 1 task.
I have 10 threads filling unique codes in 10 tables simultaneously. Each thread filling up million records. After sometimes 7 tables got filled up but the rest 3 are still filling up. I want to indulge the free 7 threads in filling up the tables simultaneously with the running 3 threads can this be done??
String noOfCodes = ""+((Integer.parseInt(totalOfCodes))/10);
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
String threadNo = ""+i;
Runnable worker = new CodeGeneratorDAO(pgmId, digits, points, validity, noOfCodes, product, threadNo);
executor.execute(worker);
resp.setSuccess(true);
}
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Finished all threads");
A simple solution is to define a Runnable executing a smaller task that your current Runnable. Breaking down the tasks will smooth the overall execution time.
You say that your Runnable "fills up 1000 records", so define your Runnable as filling up 1 record and submit all your 10 * 1000 records to be updated to your ExecutorService:
ExecutorService executor = Executors.newFixedThreadPool(10);
for(Runnable oneRecordRunnable : allRunnables) {
executor.submit(oneRecordRunnable);
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.HOURS);
As a side note, I replaced your cpu-burning while(true) loop by the awaitTermination method.