1

I've written a simple JavaFX application that is obviously running on FX application thread. The application needs some background processing in an infinite loop running on a separate thread(not FX thread) where I call Platform.runLater() to update the gui controls of the FX application after a fixed interval. If I close the FX Gui Application, the background thread continues it execution.

In order to terminate the background thread after FX thread has terminated, I am now using fxThread.isAlive() in the while loop on the background thread. This way, the background thread automatically terminates as soon as the FX Thread is terminated as the while loop condition becomes false.

Is this a bad choice? What are the alternative and efficient ways to accomplish the same task?

//imports
public class SimpleClockFX implements Application{  
Thread fxThread;  
 //other variables  

@Override  
public void start(Stage primaryStage){  
    fxThread = Thread.currentThread();  

    //other stuff...  

    new Thread(()->{  
    //logic...  
      while(fxThread.isAlive()){  
          //logic...  
                  Platform.runLater(()->{
                      //update gui controls
                  });
      }  
    }).start();  

}

Umar Tahir
  • 333
  • 1
  • 7
  • 15

2 Answers2

3

By calling the fxThread.isAlive() its not quite the best solution , cause in the worst scenario , your fxThread might die, while the thread has passed the fxThread.isAlive() and in the time entering the Platform.runLater will give you an exception , except if this is the proper termination for your case

try adding a listener for the close event on the top level stage.

Also call System.exit(0) to terminate completely the JVM or whatever custom termination methods you want(f.e. explicitly cause interrupt to the background thread if its still running).

    @Override
    public void start(Stage primaryStage) 
    {
        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            public void handle(WindowEvent we) {
                System.out.println("Stage is closing");
                System.exit(0);
            }
        });  

        primaryStage.setTitle("Hello World!");
//      add your components
        primaryStage.show();
//      not daemon
        new Thread(new CustomRunnable()).start();

    }

    private static class CustomRunnable implements Runnable
    {
        public void run() 
        {

            while(true){
//              long operation
            }
        }
    }

EDIT :

As per @lostsoul29 comments , the scenario implies that the spawning threads are not going to be daemon threads. In case that any thread is marked as daemon , it will require custom termination / handling.

AntJavaDev
  • 1,204
  • 1
  • 18
  • 24
  • Seems Good.... Didn't know that setOnCloseRequest() exists. I've just added the following snippet: primaryStage.setOnCloseRequest((WindowEvent we) ->{ System.exit(0); }); and updated the while loop condition to "true"... And it's working the job pretty well. Thank You :) – Umar Tahir Jul 01 '16 at 17:20
  • `System.exit(0)` doesn't kill all spawned child threads. You must close each of the threads in turn and then exit the application. – lostsoul29 May 02 '17 at 21:47
  • @lostsoul29 , well seems that you haven't understood the question/answer quite clearly. As a start , you could refer to the links below , and if you still face issues , please let us know , with a new question. [Thread Life-Cycle](https://docs.oracle.com/cd/E82638_01/JJDEV/threading-in-database.htm#JJDEV-GUID-44A07CEA-EB31-4C69-9300-6068A63EC880) , [setDaemon()](https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#setDaemon(boolean)) – AntJavaDev May 04 '17 at 16:32
3

If the thread doesn't need to do any cleanup on termination, then just make the thread a daemon thread:

thread.setDaemon(true)

Marks this thread as either a daemon thread or a user thread. The Java Virtual Machine exits when the only threads running are all daemon threads. This method must be invoked before the thread is started.

Using setDaemon() is by far the easiest thing to do and is what is recommended if your processing thread does not need to do any cleanup (e.g. it does not need to complete or roll-back an atomic commit transaction) before the application exits.


If you need to perform cleanup for the thread before it exits, then it is better to not make the thread a daemon, but instead make the thread interruptible, issue an interrupt and handle the interrupt.

For example, manage your thread using an ExecutorService, that you shutdown in the Application stop() method using a method similar to that mentioned in the ExecutorService javadoc:

void shutdownAndAwaitTermination(ExecutorService pool) {
  pool.shutdown(); // Disable new tasks from being submitted
  try {
    // Wait a while for existing tasks to terminate
    if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
      pool.shutdownNow(); // Cancel currently executing tasks
      // Wait a while for tasks to respond to being cancelled
      if (!pool.awaitTermination(60, TimeUnit.SECONDS))
          System.err.println("Pool did not terminate");
    }
  } catch (InterruptedException ie) {
    // (Re-)Cancel if current thread also interrupted
    pool.shutdownNow();
    // Preserve interrupt status
    Thread.currentThread().interrupt();
  }
}

Note that the shutdownNow() call implicitly sends interrupts to your thread, which will not be effective unless your thread processor is explicitly coded to handle the interrupt.

typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.

If the cancellation really isn't working and you just want to give up, you can replace the System.err.println() statement in the above code with a System.exit().

And your thread task logic also needs to deal with the interruption:

public class PrimeProducer extends Thread {
    private final BlockingQueue<BigInteger> queue;

    PrimeProducer(BlockingQueue<BigInteger> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            BigInteger p = BigInteger.ONE;
            while (!Thread.currentThread().isInterrupted())
                queue.put(p = p.nextProbablePrime());
        } catch (InterruptedException consumed) {
            /* Allow thread to exit */
        }
    }

    public void cancel() { interrupt(); }
}

Also note, if you aren't subclassing Thread, but are instead implementing Runnable for library type code, then you don't want to swallow the interrupt as above, but instead you want to restore the interrupted status, similar to below (read "Dealing with InterruptedException" below to further understand why restoring the interrupted status is desirable for library code):

public class TaskRunner implements Runnable {
    private BlockingQueue<Task> queue;

    public TaskRunner(BlockingQueue<Task> queue) { 
        this.queue = queue; 
    }

    public void run() { 
        try {
             while (true) {
                 Task task = queue.take(10, TimeUnit.SECONDS);
                 task.execute();
             }
         }
         catch (InterruptedException e) { 
             // Restore the interrupted status
             Thread.currentThread().interrupt();
         }
    }
}

See also some reference documents (from which some of the info in this answer was copy and pasted):

Community
  • 1
  • 1
jewelsea
  • 150,031
  • 14
  • 366
  • 406