How to safely close all threads before exit

Viewed 1198

I have an application that starts a few threads, eventually a thread may need to exit the entire application, however other threads may be in the middle of a task so I'd like to let them continue their current loop before exiting.

In the example below Thread2 has no idea when Thread1 is trying to exit, it simply forces everything to finish right away.

How can I let Thread2, 3 & 4 etc. finish up their loops before shutting down?

Edit: To address the duplicate question concerns: this varies from the typical situation in that the parent class cannot be responsible for puppeteering the shut downs, any of the individual threads must be able to initiate the shut down.

Edit2: I've also left an answer with what I ended up doing which is an implementation of the accepted answer.

class Scratch {
    public static void main(String[] args) {
        Thread Task1 = new Thread(new Task1());
        Task1.start();

        Thread Task2 = new Thread(new Task2());
        Task2.start();

        // ... more threads
    }

    public class Task1 implements Runnable {
        public void run() {
            while (true) {
                // ...
                System.exit(0);
                // ...
            }
        }
    }

    public class Task2 implements Runnable {
        public void run() {
            while (true) {
                // ...
                // I need to know about System.exit(0) to exit my loop
                // ...
            }
        }
    }
}
6 Answers

You can use a volatile boolean variable that all threads will constantly check. If one thread sets the value of that variable to false, all threads will see the new value and leave the while loop.

Explanation: read / write operations in volatile variables are atomic. Besides that, the value of volatile variables are not cached, so all threads see the same value.

class Scratch {

    private static volatile boolean isRunning = true;

    public static void main(String[] args) {
        Thread Task1 = new Thread(new Task1());
        Task1.start();

        Thread Task2 = new Thread(new Task2());
        Task2.start();

        // ... more threads
    }

    public class Task1 implements Runnable {
        public void run() {
            while (isRunning) {
                // ...
                isRunning = false; // let's stop all threads
                // ...
            }
        }
    }

    public class Task2 implements Runnable {
        public void run() {
            while (isRunning) {
                // ...
                // I need to know about System.exit(0) to exit my loop
                // ...
            }
        }
    }
}

Creating thread by yourself is considered as "bad practice", you should think about using an ExecutorService. The synchronization should be done via interrupting the threads.

class Scratch {
    private final ExecutorService executorService;
    public Scratch(ExecutorService es) {
       this.executorService = es;
    }
    /** Convience constructor */
    public Scratch() {this(Executors.newCachedThreadPool());}

    public class Task1 implements Callable<Void> {
        public Void call() throws Exception {
            while(true) {
                ...
                executorService.shutdownNow(); // interrupt all running threads
                                               // (including Task1) started by
                                               // given executor service
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
            return null;
        }
     }

     public class Task2 implements Callable<Void> {
         public Void call() throws Exception {
             while(true) {
                 // check if the thread was interrupted, if so throw Exception
                 if (Thread.interrupted())
                     throw new InterruptedException();
             }
             return null;
         }
     }

     public static void main(String ... args) {
         Scratch s = new Scratch();
         s.executorService.submit(new Task1());
         s.executorService.submit(new Task2());
     }
 }

Besides the method achieved by using a volatile boolean variable, which is a great one, you could also consider using listeners. They are commonly used through out the Android API.

First, create the interface that defines a listener. Here is an example:

public interface TaskListener {
    public void onFinish();
}

Now, implement this interface on the classes that you want to be notified of your task's finishing. Look below:

public class Task2 implements Runnable, TaskListener { 
    public void run() { 
        while (!Thread.currentThread().isInterrupted()) { 
            //... 
        } 
    }

    public void onFinish() {
         //perform your exit operations here.
         Thread.currentThread().interrupt();
    } 
}

Now, prepare your main task's class to receive listeners. Take a look:

public class Task1 implements Runnable { 
    private ArrayList<TaskListener> listeners = new ArrayList<>();

    public void run() { 
        while (true) { 
            // ... 
            this.finish();
            // ... 
        } 
    } 

    public void addListener (TaskListener listener) {
        this.listeners.add(listener);
    } 

    private void finish() {
        for(TaskListener listener: this.listeners) {
             listener.onFinish();
        } 
    } 
 }

With every thing set up, now it's easy to listen for your task's finishing. Look below:

public static void main(String[] args) { 
    Thread task1 = new Thread(new Task1()); 
    Thread task2 = new Thread(new Task2()); 

    task1.addListener(task2);
    task1.start();
    task2.start();
} 

Done! Now, everytime task1.finish() is called, all it's listeners (only task2 in this example) are notified (their onFinish method is called).

Note: it's not necessary to implement TaskListener in all of your tasks if you don't want to. Here is an example using a lambda expression:

public static void main(String[] args) { 
    Thread task1 = new Thread(new Task1()); 
    Thread task2 = new Thread(new Task2()); 

    task1.addListener(() -> {
        //this entire block will be called when task1 finishes
        task2.interrupt();
    });

    task1.start();
    task2.start();
} 

Note2: I wrote this answer on my phone. Although I revised the code several times and it seems OK, I won't be able to test it until I get home.

You can't do that easily.

First of all System.exit(0) exits the JVM, and it doesn't care about threads.

What you would have to do:

  • define your own framework to manage threads
  • also derive an "architecture" for your threads. Anything that is supposed to be controlled by your framework would need A) means to query "how much longer will you run?" and B) "prepare for going down"

And of course: in the real world, there might many threads running in your application that you don't even know of (for example when you used someCollection.parallelStream() somewhere: that uses a JVM wide thread pool with threads out of your control).

Some middle ground would be a "thread manager" component. Threads that do not want to be killed have to register with that manager.

But still: even that is hard to get right. What happens for example when some threads are in a deadlock? Or waiting for something "outside" of the JVM to happen? It takes a lot of effort to figure that threads are really progressing, too.

As suggested by @HugoTeixeira I ended up using volatile & atomic-integers to keep track of the threads. I also made a control thread to periodically check the values and decide if it's time to exit.

class Scratch {
    public static volatile boolean isRunning = true;
    public static AtomicInteger threadCount = new AtomicInteger();

    public static void main(String[] args) {
        Thread controlThread = new Thread(new ControlThread());
        controlThread.start();

        Thread Task1 = new Thread(new Task1());
        Task1.start();
        if(Task1.isAlive()) { threadCount.incrementAndGet(); }

        Thread Task2 = new Thread(new Task2());
        Task2.start();
        if(Task2.isAlive()) { threadCount.incrementAndGet(); }

        // ... more threads
    }
}

Task threads:

public class Task1 implements Runnable {
    public void run() {
        while (isRunning) {
            // ...
            isRunning = false; // let's stop all threads
            // ...
        }
     }
  }

Control Thread:

import static Scratch.isRunning;
import static Scratch.threadCount;

public class ControlThread implements Runnable {
    public void run() {
        while(true) {
            try {
                // Check if we have stopped running the app AND all threads have finished up.
                if(!isRunning && threadCount.get() == 0) {
                    // Safely close the app
                    System.exit(0);
                }
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                // Ignore exceptions
            }
        }
    }
}

If the intention is to wait all Thread to complete execution. Consider using ExecutorService and Future. The Sketch below represents general idea of implementing it.

final ExecutorService pool = Executors.newCachedThreadPool();

Future<?> task1 = pool.submit(() -> {
    //do loop task1
});

Future<?> task2 = pool.submit(() -> {
    //do loop task2
});

while(!task1.isDone() && !task2.isDone()){
   //waits to complete
}

After all tasks completed, shutdown the executor using pool.shutdown(). However to this is just simple example. Refer ExecutorService javadoc for more details.

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html

Related