再论Java Swing线程

欢迎进入Java社区论坛,与200万技术人员互动交流 >>进入

  现在,我们添加Logger作为在FixedFrame构造方法中的LookupManager的一个监听器。

  public FixedFrame() {

  lookupManager = new LookupManager();

  lookupManager.addListener(this);

  lookupManager.addListener(new Logger());

  initComponents();

  layoutComponents();

  }

  现在你已经看到了添加新的事件、创建新的监听器--向您展示了事件驱动方案的灵活性和扩展性。你会发现随着你更多地开发事件集中的程序,你会更加娴熟地在你的应用中创建通用动作。像其它所有事情一样,这只需要时间和经验。看起来在事件模型上已经做了很多研究,但是你还是需要把它和其它替代方案相比较。考虑开发时间成本;最重要的,这是一次性成本。一旦你创建好了监听器模型和它们的动作,以后向你的应用中添加监听器将是小菜一蝶。

  线程

  到现在,我们已经解决了上面的异步问题;通过监听器使组件脱耦,通过事件对象传递变量,通过事件产生和监听器的注册的组合决定执行的顺序。让我们回到线程问题,因为正是它把我们带到了这儿。实际上非常容易:因为我们已经有了异步功能的监听器,我们可以简单地让监听器自己决定它们应该在哪个线程中执行。考虑UI类和LookupManager的分离。UI类基于事件,决定需要什么处理。并且,该类也是Swing,而日志类不是。所以让UI类负责决定它应该在什么线程中执行将更加有意义。所以,让我们再次看看UI类。下面是没有线程的lookupCompleted()方法:

  public void lookupCompleted(final LookupEvent e) {

  outputTA.setText(“”);

  String[] results = e.getResults();

  for (int i = 0; i < results.length; i++) {

  String result = results[i];

  outputTA.setText(outputTA.getText() + ” ” + result);

  }

  }

  我们知道这将在非Swing线程中调用,因为该事件是直接在LookupManager中触发的,这将不是在Swing线程中执行。因为所有的代码功能上都是异步的(我们不必等待监听器方法允许结束后才调用其它代码),我们可以通过SwingUtilities.invokeLater()将这些代码改道到Swing线程。下面是新的方法,传入一个匿名Runnable到SwingUtilities.invokeLater():

  public void lookupCompleted(final LookupEvent e) {

  //notice the threading

  SwingUtilities.invokeLater( new Runnable() {

  public void run() {

  outputTA.setText(“”);

  String[] results = e.getResults();

  for (int i = 0; i < results.length; i++) {

  String result = results[i];

  outputTA.setText(outputTA.getText() + ” ” + result);

  }

  }

  }

  );

  }

  如果任何LookupListener不是在Swing线程中执行,我们可以在调用线程中执行监听器代码。作为一个原则,我们希望所有的监听器都迅速地接到通知。所以,如果你有一个监听器需要很多时间来处理自己的功能,你应该创建一个新的线程或者把耗时代码放入ThreadPool中等待执行。

  最后的步骤是让LookupManager在非Swing线程中执行lookup。当前,LookupManager是在JButton的ActionListener的Swing线程中被调用的。现在是我们做出决定的时候,或者我们在JButton的ActionListener中引入一个新的线程,或者我们可以保证lookup自己在非Swing线程中执行,自己开始一个新的线程。我选择尽可能和Swing类贴近地管理Swing线程。这有助于把所有Swing逻辑封装在一起。如果我们把Swing线程逻辑添加到LookupManager,我们将引入了一层不必要的依赖。并且,对于LookupManager在非Swing线程环境中孵化自己的线程是完全没有必要的,比如一个非绘图的用户界面,在我们的例子中,就是Logger。产生不必要的新线程将损害到你应用的性能,而不是提高性能。LookupManager执行的很好,不管Swing线程与否--所以,我喜欢把代码集中在那儿。

  现在我们需要将JButton的ActionListener执行lookup的代码放在一个非Swing线程中。我们创建一个匿名的Thread,使用一个匿名的Runnable执行这个lookup。

  private void searchButton_actionPerformed() {

  new Thread(){

  public void run() {

  lookupManager.lookup(searchTF.getText());

  }

  }.start();

  }

  这就完成了我们的Swing线程。简单地在actionPerformed()方法中添加线程,确保监听器在新的线程中执行照顾到了整个线程问题。注意,我们不用处理像第一个例子那样的任何问题。通过把时间花费在定义一个事件驱动的体系,我们在和Swing线程相关处理上节约了更多的时间。

  结论

  如果你需要在同一个方法中执行大量的Swing代码和非Swing代码,很容易将某些代码放错位置。事件驱动的方式将迫使你将代码放在它应该在的地方--它仅应该在的地方。如果你在同一个方法中执行数据库调用和更新UI组件,那么你就在一个类中写入了太多的逻辑。分析你系统中的事件,创建底层的事件模型将迫使你将代码放到正确的地方。将费时的数据库调用代码放在非UI类中,也不要在非UI组件中更新UI组件。采用事件驱动的体系,UI负责UI更新,数据库管理类负责数据库调用。在这一点上,每一个封装的类都只用关心自己的线程,不用担心系统其它部分如何动作。当然,设计、构建一个事件驱动的客户端也很有用,但是需要花费的时间代价远超过带来的结果系统的灵活性和可维护性的提高。

[1][2][3][4]

带上心灵去旅行,以平和的心态看待一切,

再论Java Swing线程

相关文章:

你感兴趣的文章:

标签云: