I have a method and I want to run only the parts of the method at the same time. For example, I want to run doSomethingOne, doSomethingTwo and doSomethingThree methods at the same time, but they should start after the start variable and end before the end variable.
public static void someMethod() {
final ExecutorService pool = Executors.newFixedThreadPool(15);
long start = System.nanoTime();
pool.execute(() -> {
doSomethingOne;
});
pool.execute(() -> {
doSomethingTwo;
});
pool.execute(() -> {
doSomethingThree;
});
pool.shutdown();
long end = System.nanoTime();
System.out.println((end - start) / 1_000_000_000L);
}
Your current code will already let all three threads start after start
is initialized. However, the shutdown
method doesn't wait for all the work to be done.
CountDownLatch
You can use a CountDownLatch
to wait until all jobs are done:
public static void someMethod() {
final ExecutorService pool = Executors.newFixedThreadPool(15);
final CountDownLatch endLatch = new CountDownLatch(3);
long start = System.nanoTime();
pool.execute(() -> {
try {
doSomethingOne;
} finally {
endLatch.countDown();
}
});
pool.execute(() -> {
try {
doSomethingTwo;
} finally {
endLatch.countDown();
}
});
pool.execute(() -> {
try {
doSomethingThree;
} finally {
endLatch.countDown();
}
});
pool.shutdown();
// exception handling omitted
endLatch.await();
long end = System.nanoTime();
System.out.println((end - start) / 1_000_000_000L);
}
There are two things that can mess with timing: the time it takes to schedule the tasks, and the time it takes to shutdown the pool. Another CountDownLatch
can help with the first, and reordering the shutdown
call with the other:
public static void someMethod() {
final ExecutorService pool = Executors.newFixedThreadPool(15);
try {
final CountDownLatch startLatch = new CountDownLatch(1);
final CountDownLatch endLatch = new CountDownLatch(3);
pool.execute(() -> {
try {
startLatch.await(); // exception handling omitted
doSomethingOne;
} finally {
endLatch.countDown();
}
});
pool.execute(() -> {
try {
startLatch.await(); // exception handling omitted
doSomethingTwo;
} finally {
endLatch.countDown();
}
});
pool.execute(() -> {
try {
startLatch.await(); // exception handling omitted
doSomethingThree;
} finally {
endLatch.countDown();
}
});
startLatch.countDown();
long start = System.nanoTime();
// exception handling omitted
endLatch.await();
long end = System.nanoTime();
System.out.println((end - start) / 1_000_000_000L);
} finally {
pool.shutdown();
}
}
The start latch acts like a GO signal - everything can prepare itself as necessary, but only actually start when the GO is given.
Of course, with 3 almost identical blocks of code, you'd create a utility method for that, which only takes the doSomethingOne
part.