Swing threads

Event Dispatch(n. 派遣;急件) Thread

In a Swing application, most of the processing takes place in a single, special thread called the event dispatch thread (EDT).
This thread becomes active after a component(n. 成分;组件;[电子] 元件) becomes realized(vt. 实现;认识到;了解;将某物卖得,把(证券等)变成现钱;变卖): either pack, show, or setVisible(true) has been called. When a top level window is realized, all of its components are also realized. Swing is mostly single-threaded: almost all calls to realized components should execute in the event dispatch thread. The thread-safe exceptions are:

  • some methods of JComponent: repaint, revalidate, invalidate
  • all addXXXListener and removeXXXListener methods
  • all methods explicitly documented as thread-safe

Worker Threads Keep GUIs Responsive

If a task needs a relatively(adv. 相当地;相对地,比较地) long time to complete, then performing that task in the event dispatch thread will cause the user interface to become unresponsive for the duration of the task - the GUI becomes “locked”. Since this is undesirable, such tasks are usually performed outside the event dispatch thread, on what is commonly referred to as a worker thread.
When a worker thread completes its task, it needs a special mechanism for updating realized GUI components, since, as stated above, realized components almost always need to be updated only from the event dispatch thread.
With modern JDK’s, the most common way of doing this is the SwingWorker class, introduced in JSE 6. It should be used if available.
For using timers, see this related topic.

Example 1

The FetchQuotesAction fetches stock price information. This is a good example of an operation which should be on a separate thread. Since it’s not known how long it will take, or even if a good web connection is present, it’s an operation which could very easily lock the GUI.
Below are the pertinent(adj. 相关的,相干的;中肯的;切题的) parts of its code. Note how a SwingWorker is divided into two parts: doInBackground does the main work and returns any needed data, and done first calls get to access the data, and then updates the user interface.

/**
* Fetch current quote(n. 引用) data for the {@link CurrentPortfolio} from a data 
* source on the web.
*/
public final class FetchQuotesAction extends AbstractAction implements Observer {

  //elided(vt. 省略;取消;删去;不予考虑删节)...

  /** 
   Fetch quotes from the web for the <tt>CurrentPortfolio</tt>.
   This is called either explicitly, or periodically, by a Timer.
  */
  @Override public void actionPerformed(ActionEvent e) {
    fLogger.info("Fetching quotes from web.");
    fSummaryView.showStatusMessage("Fetching quotes...");
    SwingWorker<List<Quote>, Void> hardWorker = new HardWorker();
    hardWorker.execute();
  }

  /**
  * The set of {@link Stock} objects in which the user 
  * is currently interested.
  */
  private CurrentPortfolio fCurrentPortfolio;

  /**
  * GUI element which is updated whenever a new set of quotes is obtained.
  */
  private SummaryView fSummaryView;

  //elided...

  private final class HardWorker extends SwingWorker<java.util.List<Quote>, Void> {
    @Override  protected List<Quote> doInBackground() throws Exception {
      List<Quote> result = null;
      try {
        result = fCurrentPortfolio.getPortfolio().getQuotes();
      }
      catch(DataAccessException ex){
        ex.printStackTrace();
      }
      return result;
    }
    @Override protected void done() {
      try {
        //get the data fetched above, in doInBackground()
        List<Quote> quotes = get();
        if (quotes != null){
          showUpdated(quotes);
        }
        else {
          fSummaryView.showStatusMessage("Failed - Please connect to the web.");
        }
      }
      catch (Exception ex) {
        ex.printStackTrace();
      }
    }
  }
}

JDK < 6

Other techniques are needed when using older versions of the JDK. Two methods of the EventQueue class, invokeLater and invokeAndWait, are provided for this purpose (SwingUtilities has synonymous(adj. 同义的;同义词的;同义突变的) methods as well). Sun recommends using invokeLater as the usual preferred style.
When using threads in this way, it is usually a good idea to use daemon threads, not user threads, whenever possible: daemon threads will not prevent an application from terminating. Since threads are user threads by default, an explicit call to Thread.setDaemon(true) is required.
The remaining examples use JSE 1.5.

Example 2

The Splash Screen topic (and in particular its Launcher class) is a good example of using a worker thread. Here, the status of the launch thread as a worker thread is exploited(vt. 开发,开拓;剥削;开采) to show a splash screen to the user, but only until the main window has finished loading.

Example 3

The ColorTip class, shown below, changes the background color of a component for a short, fixed interval(n. 间隔;间距;幕间休息) of time, as a simple way of calling attention to that component.

Its worker thread does not work very hard - it sleeps a lot. The calls to sleep do not cause the GUI to become unresponsive, however, since these calls do not take place in the event dispatch thread.

ColorTip has three private, Runnable nested classes :

  • Worker - inserts specific time intervals between changing colors
  • ChangeColor - updates the GUI by changing the background color of a target component
  • RevertColor - updates the GUI by changing the background color back to its original color
package hirondelle.stocks.quotes;

import hirondelle.stocks.util.Args;
import hirondelle.stocks.util.Consts;
import hirondelle.stocks.util.Util;

import java.awt.Color;
import java.awt.EventQueue;
import java.util.logging.Logger;

import javax.swing.JComponent;

/**
* Calls user's attention to an aspect of the GUI (much like a 
* <tt>ToolTip</tt>) by changing the background color of a 
* component (typically a <tt>JLabel</tt>) for a few seconds; 
* the component will always revert to its original background color 
* after a short time has passed. This is done once, without repeating.
*
* <p>Example use case:
<pre>
 //no initial(adj. 最初的;字首的) delay, and show the new color for 2 seconds only
 ColorTip tip = new ColorTip(0, 2, someLabel, temporaryColor);
 tip.start();
</pre>
* 
* Uses a daemon thread, so this class will not prevent a program from 
* terminating. Will not lock the GUI.
*/
final class ColorTip {

  /**
  * Constructor. 
  *  
  * @param aInitialDelay number of seconds to wait before changing the 
  * background color of <tt>aComponent</tt>, and must be in range 0..60 (inclusive).
  * @param aActivationInterval number of seconds to display <tt>aTempColor</tt>, 
  * and must be in range 1..60 (inclusive).
  * @param aComponent GUI item whose background color will be changed.
  * @param aTempColor background color which <tt>aComponent</tt> will take for 
  * <tt>aActivationInterval</tt> seconds.
  */
  ColorTip (
    int aInitialDelay, int aActivationInterval, JComponent aComponent, Color aTempColor
   ) {
    Args.checkForRange(aInitialDelay, 0, Consts.SECONDS_PER_MINUTE);
    Args.checkForRange(aActivationInterval, 1, Consts.SECONDS_PER_MINUTE);
    Args.checkForNull(aTempColor);
    fInitialDelay = aInitialDelay;
    fActivationInterval = aActivationInterval;
    fComponent = aComponent;
    fTemporaryColor = aTempColor;
    fOriginalColor = aComponent.getBackground();
    fOriginalOpacity = aComponent.isOpaque();
  }

  /**
  * Temporarily(adv. 临时地,临时) change the background color of the component, without interfering(vi. 干涉;妨碍;打扰) with 
  * the user's control of the gui, and without preventing program termination.
  *
  * <P>If the target temporary color is the same as the current background color, then 
  * do nothing. (This condition occurs when two <tt>ColorTip</tt> objects are 
  * altering the same item at nearly the same time, such that they "overlap".)
  */
  void start(){
    if (isSameColor()) return;
    /*
     * The use of a low-level Thread, instead of a more modern class, is unusual here.
     * It's acceptable since other tools aren't a great match for this task, which is to 
     * go back and forth TWICE (wait, color-on, wait, color-off) between a worker thread
     * and the Event Dispatch Thread; that's not a good match for either SwingWorker 
     * or javax.swing.Timer.
     */
    Thread thread = new Thread(new Worker());
    thread.setDaemon(true);
    thread.start();
  }

  // PRIVATE 
  private final int fInitialDelay;
  private final int fActivationInterval;
  private final JComponent fComponent;
  private final Color fTemporaryColor;
  private final Color fOriginalColor;
  private final int fCONVERSION_FACTOR = Consts.MILLISECONDS_PER_SECOND;

  /**
  * Stores the original value of the opaque property of fComponent.
  *  
  * Changes to the background color of a component 
  * take effect only if the component is in charge of(负责;主管) drawing its background.
  * This is defined by the opaque property, which needs to be true for these
  * changes to take effect.
  *
  * <P>If fComponent is not opaque, then this property is temporarily
  * changed by this class in order to change the background color.
  */
  private final boolean fOriginalOpacity;

  private static final Logger fLogger = Util.getLogger(ColorTip.class);

  /**
  * Return true only if fTemporaryColor is the same as the fOriginalColor. 
  */
  private boolean isSameColor(){
    return fTemporaryColor.equals(fOriginalColor);
  }

  /** 
   The sleeping done by this class is NOT done on the Event Dispatch Thread; 
   that would lock the GUI. 
  */
  private final class Worker implements Runnable {
    @Override public void run(){
      try {
        fLogger.fine("Initial Sleeping...");
        Thread.sleep(fCONVERSION_FACTOR * fInitialDelay);
        EventQueue.invokeLater(new ChangeColor());
        fLogger.fine("Activation Sleeping...");
        Thread.sleep(fCONVERSION_FACTOR * fActivationInterval);
        EventQueue.invokeLater(new RevertColor());
      }
      catch (InterruptedException ex) {
        fLogger.severe("Cannot sleep.");
      }
      fLogger.fine("Color worker done.");
    }
  }

  private final class ChangeColor implements Runnable {
    @Override public void run(){
      if (! fOriginalOpacity) {
        fComponent.setOpaque(true);
      }
      fComponent.setBackground(fTemporaryColor);
    }
  }

  private final class RevertColor implements Runnable {
    @Override public void run(){
      fComponent.setBackground(fOriginalColor);
      fComponent.setOpaque(fOriginalOpacity);
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值