6

I want to achieve the following: When my application starts, the main thread will start 1+ worker threads that should run in the background, and periodically do things behind the scenes. These should not block the main thread: once main starts the workers, it continues doing its own thing until:

  1. The main thread finishes (normal application termination) - in the case of a command-line utility this is when the end of the main(String[]) method is reached; in the case of a Swing GUI it could be when the user selects the File >> Exit menu, etc.
  2. The operating system throws a kill command (SIGKILL, etc.)
  3. An unexpected, uncaught exception occurs in the main thread, effectively killing it (this is just an unpolite version of #1 above)

Once started/submitted from the main thread, I want all the worker threads (Runnables) to essentially have their own life cycle, and exist independently of the main thread. But, if the main thread dies at any time, I want to be able to block (if at all possible) the main thread until all the workers are finished shutting down, and then "allow" the main thread to die.

My best attempt so far, although I know I'm missing pieces here and there:

public class MainDriver {
    private BaneWorker baneWorker;

    private ExecutorService executor = Executors.newCachedThreadPool();

    public static void main(String[] args) {
        MainDriver driver = new MainDriver();
        driver.run();

        // We've now reached the end of the main method. All workers should block while they shutdown
        // gracefully (if at all possible).
        if(executor.awaitTermination(30, TimeUnit.SECONDS))
            System.out.println("Shutting down...");
        else {
            System.out.println("Forcing shut down...");
            executor.shutdownNow();
        }
    }

    private void run() {
        // Start all worker threads.
        baneWorker = new BaneWorker(Thread.currentThread());
        // More workers will be used once I get this simple example up and running...

        executor.submit(baneWorker);
        // Eventually submit the other workers here as well...

        // Now start processing. If command-line utility, start doing whatever the utility
        // needs to do. If Swing GUI, fire up a parent JFrame and draw the application to the
        // screen for the user, etc.
        doStuff();
    }

    private void doStuff() {
        // ??? whatever
    }
}

public class BaneWorker implements Runnable {
    private Timer timer;

    private TimerTask baneTask;

    private Thread mainThread;

    public BaneWorker(Thread mainThread) {
        super();

        this.mainThread = mainThread;
    }

    @Override
    public void run() {
        try {
            timer = new Timer();

            baneTask = new TimerTask() {
                @Override
                public void run() {
                    System.out.println("When the main thread is ashes...");
                }
            };

            // Schedule the baneTask to kick off every minute starting now.
            timer.scheduleAtFixedRate(baneTask, new Date(), 60 * 1000);
        } catch(InterruptedException interrupt) {
            // Should be thrown if main thread dies, terminates, throws an exception, etc.
            // Should block main thread from finally terminating until we're done shutting down.
            shutdown();
        }
    }

    private void shutdown() {
        baneTask.cancel();

        System.out.println("...then you have my permission to die.");

        try {
            mainThread.join();
        } catch(InterruptedException interrupt) {
            interrupt.printStackTrace;
        }
    }
}

Am I on-track or way off-base here? What do I need to change to make this work the way I need it to? I'm new to Java concurrency and am trying my best to use the Concurrency API correctly, but stumbling around a bit. Any ideas? Thanks in advance!

  • 2
    nice question+1, I suggest a class that manages and monitors every background threads, and when system wants to shutdown get the permission from the class and then shutdown, it could be done with either semaphore or pool too, for example check [this](http://arashmd.blogspot.com/2013/07/java-thread-example.html#fe) example it's about some threads working, thread main wait for then to join then terminate the system –  Jul 04 '13 at 14:41
  • 3
    I'm not sure you want mainthread.join() in your thread. This will make your child threads wait until the main thread exits. I thought you wanted the main thread to wait until the children exited. – Tony Ennis Jul 04 '13 at 15:10
  • 1
    Thanks @user2511414 and Tony Ennis (+1 each). Come to think of it, if all my workers are actually `Runnable`s, I don't think I can use the `Thread#join()` on them. I think the only thing I can do is call `executor#awaitTermination(...)`. **Is this true?** Thanks for any clarification here! –  Jul 04 '13 at 20:57
  • @TicketMonster exactly, you need to call the `awaitTermination()` method, but IF it doesn't act as you expect, so you need to have your own [thread pool](http://arashmd.blogspot.com/2013/06/java-threading.html#trpool) –  Jul 04 '13 at 21:49

3 Answers3

1

The main thread must signal the worker threads to terminate (generally this is achieved just by using a flag) and then it should call join on every thread to wait for their termination. Have a look here: Java: How to use Thread.join

Community
  • 1
  • 1
Emiliano
  • 22,232
  • 11
  • 45
  • 59
  • Thanks @happy_emi (+1) - please see my question under the original post - I have the same question for you! Thanks again! –  Jul 04 '13 at 20:58
1

You can use Runtime.addShutdownHook to register an un-started thread that is executed when a JVM is terminated, the system is shutting down etc. This code can do some cleanup itself, or perhaps notify running daemon threads to finish their work. Any such cleanup code must be relatively fast, because on many systems programs have only a limited time to do cleanup before they're forcibly terminated.

Perhaps you could also consider making your background thread daemon threads. Then they will not block the JVM when main finishes and will be still running during the clean-up phase.

Note that you can't intercept SIGKILL - this signal is designed to be unavoidable and immediate. But it should work with SIGTERM, SIGHUP and similar signals.


Update: You can easily create ExecutorServices that run daemon threads. All you need is to create a proper ThreadFactory:

public static class DaemonFactory
        implements ThreadFactory
    {
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setDaemon(true);
            return t;
        }
    }

than you create an ExecutorService like

public static void main(String argv[])
    throws Exception
{
    ExecutorService es 
        = Executors.newCachedThreadPool(new DaemonFactory());
    //                                  ^^^^^^^^^^^^^^^^^^^
    es.submit(new Callable<Object>() {
        public Object call() throws Exception {
            Thread.sleep(100);
            System.err.println("Daemon: " +
                Thread.currentThread().isDaemon());
            return null;
        }
    });
    // Without this, JVM will terminate before the daemon thread prints the
    // message, because JVM doesn't wait for daemon threads when
    // terminating:
    es.awaitTermination(3, TimeUnit.SECONDS);
}

Concerning Thread.join(), you shouldn't try to use it on threads managed by an ExecutorService. It's the responsibility of the executor to manage them. You have no reliable way how to enumerate its threads, the executor can create and destroy threads depending on its configuration etc. The only reliable way is to call shutdown(); and then awaitTermination(...);.

Petr
  • 62,528
  • 13
  • 153
  • 317
  • 2
    Thanks for Runtime.addShutdownHook! – Tony Ennis Jul 04 '13 at 14:48
  • Thankks @Petr Pudlak (+1) - I've decided that all my "worker threads" are just going to be `Runnable`s that I pass to an `ExecutorService`. Can you confirm that if I do this, there's no way to make them (or the `ExecutorService` a daemon? Also, please see my question under the original post - I have the same question for you - thanks again! –  Jul 04 '13 at 20:58
  • @TicketMonster I updated the answer, I added how to use daemon threads with an `ExecutorService`. – Petr Jul 05 '13 at 05:47
0

If SIGKILL is a unix "kill -9" there's nothing you can do about it.

For graceful exits, use a try/catch/finally in your main. The catch will catch your exceptions and allow you to do what needs to be done (recover? abort?) The finally will give you the hook to spin down your threads gracefully.

Reviewing your code quickly, I don't see where you're keeping track of your thread instances. You'll need those if you're going to tell them to spin down.

psuedocode:

static Main(...) {
    ArrayList threads = new ArrayList();
    try {
        for (each thread you want to spin up) {
            threads.add(a new Thread())
            }
        }
    catch { assuming all are fatal. }
    finally {
        for(each thread t in threads) {
            t.shutdown();
            t.join(); /* Be prepared to catch (and probably ignore) an exception on this, if shutdown() happens too fast! */
        }
    }
Tony Ennis
  • 12,000
  • 7
  • 52
  • 73
  • 1
    the `join()` makes the application shutdown your threads one by one although they could do the shutdown stuff parallel to one another. And maybe sending an `interrupt()` to the threads might be a good idea – Marco Forberg Jul 04 '13 at 14:54
  • According to http://docs.oracle.com/javase/tutorial/essential/concurrency/join.html, join() makes one thread *wait* on another, not shut down the other thread. – Tony Ennis Jul 04 '13 at 14:57
  • 1
    exactly. you issue one shutdwon command and wait, issue another and wait... without join you could issue all shutdown commands and would have shorter waiting time if a single thread takes longer to terminate – Marco Forberg Jul 04 '13 at 16:56