Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to safely close all threads before exit [duplicate]

Tags:

java

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
                // ...
            }
        }
    }
}
like image 239
Royal Wares Avatar asked Sep 20 '18 14:09

Royal Wares


People also ask

Does System Exit kill all threads?

exit() kills all the other threads. What you wrote is correct but it doesn't have anything to do with thread interruption. If you interrupt a thread, it will not just stop. It is up to the code running in that thread to decide what happens when it is interrupted.

How do I stop multiple threads?

One method is for a thread to call interrupt on the target thread. The target thread should periodically call Thread. interrupted and if it returns true terminate gracefully. Another approach is to use a volatile boolean flag which a thread sets to true to terminate and the target thread checks the flag periodically.

How will you make sure that main thread is the last one to exit?

thread. join() waits for the thread to finish before it continues. So the sub-thread will wait for each of the threads that it spawned to finish and then it will finish. The main thread will wait for the sub-thread to finish and will then finish last.


3 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
                // ...
            }
        }
    }
}
like image 127
HugoTeixeira Avatar answered Oct 13 '22 19:10

HugoTeixeira


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());
     }
 }
like image 35
Torsten Fehre Avatar answered Oct 13 '22 18:10

Torsten Fehre


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.

like image 1
Talendar Avatar answered Oct 13 '22 18:10

Talendar