Friday, 11 May 2012

Waiting on a Thread without blocking Java Swing EDT

Event dispatching threads are used as background thread to avoid blocking queues, or simply to avoid components from freezing while performing multi-tasking. JTabbedPane is a brilliant component to demostrate typical blocking EDT issues. Assume you have JTable on two panels placed on JTabbedPane, the first panel make adaptor call to remote server to pull collection of rows to render in the table and the second does pretty much the same thing.

Without EDT, there is a great posibility that while the first panel is building its table model with data fetched from remote server, the second tab will be blocked(freeze) to prevent access. One typical solution to this culprit is to use EventQueue.invokeLater(Runnable) method. But for a long running method, this option is not viable as it is only appropriate for simple application threads that needs to update the GUI.

A more viable approach to enable you wait on a Thread while performing a long running routine is using the worker design pattern. SwingWorker enables you perform a long running task without blocking EDT. To demostrate this, let assume this business scenario, we have a system which performs several concurrent executions. We want a button ActionEvent to call a delegated shutdown method, which will await on all the threads to complete before asking the JVM to bring the system to halt.

This request can be achieved in several ways, but because we do not want our waiting thread to block Swing EDT, we will first place the delegate call to shutdown in EventQueue.invokeLater(Runnable) and then use worker pattern to handle the long waiting thread as shown below.

» Assume this is our JButton action listner event
    closeButton.addActionListener(new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        EventQueue.invokeLater(new Runnable()
        {
          public void run()
          {
            shutDown();
          }
        });
      }
    });
» Assume this is our delegated shutdown method
 public void shutDown()
 {
    try
     {
      closeButton.setEnabled(false);//disable the button to avoid futher clicks
      ShutdownWorker task = new ShutdownWorker();
      task.execute();
    }
    catch (Exception e)
    {
      // just force it
      System.exit(0);
    }
  }
» SwingWorker handler class
  class ShutdownWorker extends SwingWorker<Void, Void>
  {
    public Void doInBackground() throws Exception
    {
       System.out.println("halting system");
       performSomeLongAwaitCall();

      return null;
    }

    public void done()
    {
      // shutdown
      System.gc();
      System.exit(0);
    }
    
    private void performSomeLongAwaitCall()
    {
      try
      {
       while(some condition is not true)
       {
         //TODO: perform the thread shutdown or 
         //await on all the threads to shutdown gracefully
         [...]
         Thread.sleep(2000);
       }
      }
      catch (InterruptedException ignore)
      {}
    }
  }
Adopting this approach for tasks that takes longer to complete is much effective for Swing UI applications. As you have seen above, we used a class which extends SwingWorker, implemented its abstract method doInBackground to handle the long awaiting call to shutting down the application to avoid blocking other events dispatched from userbility.

The approach gives the user the flexibility to perform several tasks and also add power to your application. Because this post is targeted to non-blocking Swing EDT, I will not dive more deeper into SwingWorker itself here. But surely there is more to it than just using it for controlling blocking threads. I will leave the rest to you to explore - but do hope this post has help you in some way to adapt an approach for handling Swing Event dispatching design pattern.

Disclaimer
The code above has only been written to demonstrate how to avoid waiting threads blocking Swing EDT, but not a complete working program.

No comments:

Post a Comment