Reputation: 419
I launch multiple threads from a class with a main method,
public static void main(String[] args) {
for (int i = 0; i <= ALimit - 1; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
Thread myThread= new Thread(myThreadImplementsRunnable);
myThread.start();
}
}
After the thread finishes their work, the main thread is still running. if I call a System.exit(0) after the for loop, my threads won't complete their execution, in fact, they will not even start. Is there a way to trigger a System.exit(0) after all threads finish their execution, without calling the join() method on each thread? Thank you for your help.
Upvotes: 0
Views: 1796
Reputation: 11308
There's a number of ways to do this.
1. join all spawned threads. (oh right, you didn't want this, skip to 2)
This means keeping a reference to all these threads :
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[ALimit]; // array to keep track of our threads, or we could use a Collection
for (int i = 0; i < ALimit; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
Thread myThread= new Thread(myThreadImplementsRunnable);
threads[i] = myThread; // remember it
myThread.start();
}
for (Thread thread : threads) {
thread.join(); // wait until the thread finishes, will return immediately if it's already finished.
}
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
2. Use a CountDownLatch :
A CountDownLatch
is basically a count down, and threads can wait for the count down to reach 0. So if each threads that finishes counts down, the main can wait for 0;
public static void main(String[] args) throws InterruptedException {
CountDownLatch finishedRunning = new CountDownLatch(ALimit); // Latch with ALimit countdowns needed to flip
for (int i = 0; i < ALimit; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
Thread myThread= new Thread(() -> {
try {
myThreadImplementsRunnable.run();
} finally {
finishedRunning.countDown(); // one less to wait for, in a finally block, so exceptions don't mess up our count
}
};
myThread.start();
}
finishedRunning.await(); // waits until the count is 0
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
3. Use an ExecutorService and shutdown :
An ExecutorService
manages the threads for you, you can execute tasks, then simply wait for the ExecutorService
to terminate;
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(ALimit);
for (int i = 0; i < ALimit; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
executorService.execute(myThreadImplementsRunnable);
}
executorService.shutdown(); // will stop accepting new tasks, but all submitted tasks so far, will still be executed
boolean terminated = executorService.awaitTermination(3, TimeUnit.MINUTES); // we have to specify a timeout, returns a boolean which we can use to test whether it timed out or not, to maybe try and force termination
if (!terminated) {
// try and force things? Shut down anyway? log and wait some more?
}
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
4. Use an ExecutorService and futures (this is really like joining all threads again, so you may want to skip this) :
An ExecutorService
manages the threads for you, you can submit tasks, keep track of the returned Future
s, then simply wait for the result of each Future
to arrive;
public static void main(String[] args) throws InterruptedException {
Set<Future<?>> futures = new HashSet<>();
ExecutorService executorService = Executors.newFixedThreadPool(ALimit);
for (int i = 0; i < ALimit; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
Future<?> future = executorService.submit(myThreadImplementsRunnable);
futures.add(future); // remember the future, pun intended ;)
}
executorService.shutdown(); // make sure the services terminates its threads when they're no longer needed.
for (Future<?> future : futures) {
try {
future.get();
} catch (ExecutionException e) {
// task failed with an exception : e.getCause() to see which
}
}
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
A variation on this is to wrap the ExecutorService
in a CompletionService
, which will return you the Future
s in the order they finish :
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(ALimit);
CompletionService<Void> completionService = new ExecutorCompletionService<Void>(executorService);
for (int i = 0; i <= ALimit - 1; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
completionService.submit(myThreadImplementsRunnable, null);
}
executorService.shutdown(); // make sure the services terminates its threads when they're no longer needed.
for (int i = 0; i < ALimit; i++) {
Future<?> future = completionService.take();
try {
future.get();
} catch (ExecutionException e) {
// task failed with an exception : e.getCause() to see which
}
}
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
5. Use CompletableFutures
Java 8 brought us CompletableFuture
s, which allow us to use running tasks as building blocks. We can simply build a CompletableFuture
represents all of our async tasks.
public static void main(String[] args) throws InterruptedException {
CompletableFuture<?>[] completableFutures = new CompletableFuture<?>[ALimit];
for (int i = 0; i <ALimit; i++) {
MyThreadImplementsRunnable myThreadImplementsRunnable= new MyThreadImplementsRunnable();
completableFutures[i] = CompletableFuture.runAsync(myThreadImplementsRunnable);
}
CompletableFuture<Void> all = CompletableFuture.allOf(completableFutures);
try {
all.get(); // get the 'combined' result
} catch (ExecutionException e) {
// task failed with an exception : e.getCause() to see which
}
System.exit(0); // all threads have finished, frankly it's a bit superfluous now.
}
Conclusion
CountDownLatch
is probably what you want, it's simple, and there's a minimal overhead.
ExecutorService
is what a professional would use, it clearly separates the notion of thread and task, offers the option of working with Future
s, which you can cancel and which offers exception handling for the individual tasks. Threads can be reused, and the amount of threads can be tailored, independently of the number of tasks. But all this may simply be overkill.
CompletionService
is ideal for when you need to handle finishing of tasks as soon as they are finsihed.
CompletableFuture
offers the simplicity of CountDownLatch
, but threads are managed for you.
Upvotes: 2
Reputation: 8686
Use ExecutorService and thread pool. Do not forget to shut it down. See the example here https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html.
E.g.
public class MyRunnable implements Runnable{
String name = "DefaultName";
public MyRunnable(String name){
this.name = name;
}
@Override
public void run() {
for(int i = 0; i < 10; i++){
System.out.println(String.valueOf(i) + "# My Name: " + name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(new MyRunnable("John"));
pool.execute(new MyRunnable("Jimm"));
pool.execute(new MyRunnable("Billy"));
pool.shutdown();
}
}
Once all yor threads are done, your main method is finished and the execution is completed.
Upvotes: 1