⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 线程与swing.txt

📁 线程与Swing。 Swing API的设计目标是强大、灵活和易用。特别地
💻 TXT
📖 第 1 页 / 共 2 页
字号:
            myStrings[1] =  
               textField1.getText(); 
        } 
    }; 
    SwingUtilities.invokeAndWait 
      (getTextFieldText); 

    System.out.println(myStrings[0]  
                       + " " + myStrings[1]); 
} 

如果你能避免使用线程,最好这样做。线程可能难于使用,并使得程序的debug更困难。一般来说,对于严格意义下的GUI工作,线程是不必要的,比如对组件属性的更新。
不管怎么说,有时候线程是必要的。下列情况是使用线程的一些典型情况:

执行一项费时的任务而不必将事件派发线程锁定。例子包括执行大量计算的情况,会导致大量类被装载的情况(如初始化),和为网络或磁盘I/O而阻塞的情况。

重复地执行一项操作,通常在两次操作间间隔一个预定的时间周期。

要等待来自客户的消息。

你可以使用两个类来帮助你实现线程:

SwingWorker:创建一个后台线程来执行费时的操作。

Timer:创建一个线程来执行或多次执行某些代码,在两次执行间间隔用户定义的延迟。


使用SwingWorker类

SwingWorker类在SwingWorker.java中实现,这个类并不包含在Java的任何发行版中,所以你必须单独下载它。
SwingWorker类做了所有实现一个后台线程所需的肮脏工作。虽然许多程序都不需要后台线程,后台线程在执行费时的操作时仍然是很有用的,它能提高程序的性能观感。
SwingWorker's get() method. Here's an example of using SwingWorker:
要使用SwingWorker类,你首先要实现它的一个子类。在子类中,你必须实现construct()方法还包含你的长时间操作。当你实例化SwingWorker的子类时,SwingWorker创建一个线程但并不启动它。你要调用你的SwingWorker对象的start()方法来启动线程,然后start()方法会调用你的construct()方法。当你需要construct()方法返回的对象时,可以调用SwingWorker类的get()方法。这是一个使用SwingWorker类的例子:


...// 在main方法中: 
    final SwingWorker worker =  
      new SwingWorker() { 
        public Object construct() { 
            return new  
               expensiveDialogComponent(); 
        } 
    }; 
    worker.start(); 

...// 在动作事件处理方法中: 
    JOptionPane.showMessageDialog 
        (f, worker.get()); 

当程序的main()方法调用start()方法,SwingWorker启动一个新的线程来实例化ExpensiveDialogComponent。main()方法还构造了由一个窗口和一个按钮组成的GUI。
当用户点击按钮,程序将阻塞,如果必要,阻塞到ExpensiveDialogComponent创建完成。然后程序显示一个包含ExpensiveDialogComponent的模式对话框。你可以在MyApplication.java找到整个程序。

使用Timer类

Timer类通过一个ActionListener来执行或多次执行一项操作。你创建定时器的时候可以指定操作执行的频率,并且你可以指定定时器的动作事件的监听者(action listener)。启动定时器后,动作监听者的actionPerformed()方法会被(多次)调用来执行操作。
定时器动作监听者(action listener)定义的actionPerformed()方法将在事件派发线程中调用。这意味着你不必在其中使用invokeLater()方法。
这是一个使用Timer类来实现动画循环的例子:


public class AnimatorApplicationTimer  
  extends JFrame implements  
  ActionListener { 
    ...//在这里定义实例变量 
    Timer timer; 

    public AnimatorApplicationTimer(...) { 
        ... 
        // 创建一个定时器来   
        // 来调用此对象action handler。 
        timer = new Timer(delay, this); 
        timer.setInitialDelay(0); 
        timer.setCoalesce(true); 
        ... 
    } 

    public void startAnimation() { 
        if (frozen) { 
            // 什么都不做。应用户要求  
            // 停止变换图像。 
        } else { 
            // 启动(或重启动)动画! 
            timer.start(); 
        } 
    } 

    public void stopAnimation() { 
        // 停止动画线程。 
        timer.stop(); 
    } 

    public void actionPerformed 
      (ActionEvent e) { 
        // 进到下一帧动画。 
        frameNumber++; 

        // 显示。 
        repaint(); 
    } 
    ... 
} 

在一个线程中执行所有的用户界面代码有这样一些优点:

组件开发者不必对线程编程有深入的理解:像ViewPoint和Trestle这类工具包中的所有组件都必须完全支持多线程访问,使得扩展非常困难,尤其对不精通线程编程的开发者来说。最近的一些工具包如SubArctic和IFC,都采用和Swing类似的设计。

事件以可预知的次序派发:invokeLater()排队的runnable对象从鼠标和键盘事件、定时器事件、绘制请求的同一个队列派发。在一些组件完全支持多线程访问的工具包中,组件的改变被变化无常的线程调度程序穿插到事件处理过程中。这使得全面测试变得困难甚至不可能。

更低的代价:尝试小心锁住临界区的工具包要花费实足的时间和空间在锁的管理上。每当工具包中调用某个可能在客户代码中实现的方法时(如public类中的任何public和protected方法),工具包都要保存它的状态并释放所有锁,以便客户代码能在必要时获得锁。当控制权交回到工具包,工具包又必须重新抓住它的锁并恢复状态。所有应用程序都不得不负担这一代价,即使大多数应用程序并不需要对GUI的并发访问。

这是的SubArctic Java Toolkit的作者对在工具包中支持多线程访问的问题的描述:
我们的基本信条是,当设计和建造多线程应用程序,尤其是那些包括GUI组件的应用程序时,必须保证极端小心。线程的使用可能会很有欺骗性。在许多情况下,它们表现得能够极好的简化编成,使得设计“专注于单一任务的简单自治实体”成为可能。在一些情况下它们的确简化了设计和编码。然而,在几乎所有的情况下,它们都使得调试、测试和维护的困难大大增加甚至成为不可能。无论大多数程序员所受的训练、他们的经验和实践,还是我们用来帮助自己的工具,都不是能够用来对付非决定论的。例如,全面测试(这总是困难的)在bug依赖于时间时是几乎不可能的。尤其对于Java来说,一个程序要运行在许多不同类型的机器的操作系统平台上,并且每个程序都必须在抢先和非抢先式调度下都能正常工作。
由于这些固有的困难,我们力劝你三思是否绝对有使用线程的必要。尽管如此,有些情况下使用线程是必要的(或者是被其他软件包强加的),所以subArctic提供了一个线程安全的访问机制。本章讨论了这一机制和怎样在一个独立线程中安全地操作交互树。
他们所说的线程安全机制非常类似于SwingUtilities类提供的invokeLater()和invokeAndWait()方法。 
 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -