一篇很好的介绍swing下的线程问题的文章,原理跟各个方面都提及到了进行swing编程不可能不遇上线程问题
Swing API的设计目标是强大、灵活和易用。特别地,我们希望能让程序员们方便地建立新的Swing组件,不论是从头开始还是通过扩展我们所提供的一些组件。
出于这个目的,我们不要求Swing组件支持多线程访问。相反,我们向组件发送请求并在单一线程中执行请求。
本文讨论线程和Swing组件。目的不仅是为了帮助你以线程安全的方式使用Swing API,而且解释了我们为什么会选择现在这样的线程方案。
本文包括以下内容:
单线程规则:Swing线程在同一时刻仅能被一个线程所访问。一般来说,这个线程是事件派发线程(event-dispatching thread)。
规则的例外:有些操作保证是线程安全的。
事件分发:如果你需要从事件处理(event-handling)或绘制代码以外的地方访问UI,那么你可以使用SwingUtilities类的invokeLater()或invokeAndWait()方法。
创建线程:如果你需要创建一个线程——比如用来处理一些耗费大量计算能力或受I/O能力限制的工作——你可以使用一个线程工具类如SwingWorker或Timer。
为什么我们这样实现Swing:我们将用一些关于Swing的线程安全的背景资料来结束这篇文章。
Swing的规则是:
一旦Swing组件被具现化(realized),所有可能影响或依赖于组件状态的代码都应该在事件派发线程中执行。
这个规则可能听起来有点吓人,但对许多简单的程序来说,你用不着为线程问题操心。在我们深入如何撰写Swing代码之前,让我们先来定义两个术语:具现化(realized)和事件派发线程(event-dispatching thread)。
具现化的意思是组建的paint()方法已经或可能会被调用。一个作为顶级窗口的Swing组件当调用以下方法时将被具现化:setVisible (true)、show()或(可能令你惊奇)pack()。当一个窗口被具现化,它包含的所有组件都被具现化。另一个具现化一个组件的方法是将它放入到一个已经具现化的容器中。稍后你会看到一些对组件具现化的例子。
事件派发线程是执行绘制和事件处理的线程。例如,paint()和actionPerformed()方法会自动在事件派发线程中执行。另一个将代码放到事件派发线程中执行的方法是使用SwingUtilities类的invokeLater()方法。
所有可能影响一个已具现化的Swing组件的代码都必须在事件派发线程中执行。但这个规则有一些例外:
有些方法是线程安全的:在Swing API的文档中,线程安全的方法用以下文字标记:
This method is thread safe, although most Swing methods are not.
(这个方法是线程安全的,尽管大多数Swing方法都不是。)
一个应用程序的GUI常常可以在主线程中构建和显示:下面的典型代码是安全的,只要没有(Swing或其他)组件被具现化:
public class MyApplication
{
public static void main(String[] args)
{
JFrame f = new JFrame("Labels"); // 在这里将各组件
// 加入到主框架……
f.pack();
f.show();
// 不要再做任何GUI工作……
}
}
上面所示的代码全部在“main”线程中运行。对f.pack()的调用使得JFrame以下的组件都被具现化。这意味着,f.show()调用是不安全的且应该在事件派发线程中执行。尽管如此,只要程序还没有一个看得到的GUI,JFrame或它的里面的组件就几乎不可能在f.show()返回前收到一个paint()调用。因为在f.show()调用之后不再有任何GUI代码,于是所有GUI工作都从主线程转到了事件派发线程,因此前面所讨论的代码实际上是线程安全的。
一个applet的GUI可以在init()方法中构造和显示:现有的浏览器都不会在一个applet的 init()和start()方法被调用前绘制它。因而,在一个applet的init()方法中构造GUI是安全的,只要你不对applet中的对象调用show()或setVisible(true)方法。
要顺便一提的是,如果applet中使用了Swing组件,就必须实现为 JApplet的子类。并且,组件应该添加到的JApplet内容窗格(content pane)中,而不要直接添加到JApplet。对任何 applet,你都不应该在init()或start()方法中执行费时的初始化操作;而应该启动一个线程来执行费时的任务。
下述 JComponent方法是安全的,可以从任何线程调用:repaint()、revalidate()、和invalidate()。repaint ()和revalidate()方法为事件派发线程对请求排队,并分别调用paint()和validate()方法。invalidate()方法只在需要确认时标记一个组件和它的所有直接祖先。
监听者列表可以由任何线程修改:调用addListenerTypeListener()和removeListenerTypeListener()方法总是安全的。对监听者列表的添加/删除操作不会对进行中的事件派发有任何影响。
注意:revalidate()和旧的validate()方法之间的重要区别是,revalidate()会缓存请求并组合成一次validate()调用。这和repaint()缓存并组合绘制请求类似。
大多数初始化后的GUI工作自然地发生在事件派发线程。一旦GUI成为可见,大多数程序都是由事件驱动的,如按钮动作或鼠标点击,这些总是在事件派发线程中处理的。
不过,总有些程序需要在GUI成为可见后执行一些非事件驱动的GUI工作。比如:
在成为可用前需要进行长时间初始化操作的程序:这类程序通常应该在初始化期间就显示出GUI,然后更新或改变GUI。初始化过程不应该在事件派发线程中进行;否则,重绘组件和事件派发会停止。尽管如此,在初始化之后,GUI的更新/改变还是应该在事件派发线程中进行,理由是线程安全。
必须响应非AWT事件来更新GUI的程序:例如,想象一个服务器程序从可能运行在其他机器上的程序得到请求。这些请求可能在任何时刻到达,并且会引起在一些可能未知的线程中对服务器的方法调用。这个方法调用怎样更新GUI呢?在事件派发线程中执行GUI更新代码。
SwingUtilities类提供了两个方法来帮助你在事件派发线程中执行代码:
invokeLater():要求在事件派发线程中执行某些代码。这个方法会立即返回,不会等待代码执行完毕。
invokeAndWait():行为与invokeLater()类似,除了这个方法会等待代码执行完毕。一般地,你可以用invokeLater()来代替这个方法。
下面是一些使用这几个API的例子。请同时参阅《The Java Tutorial》中的“BINGO example”,尤其是以下几个类:CardWindow、ControlPane、Player和OverallStatusPane。
使用invokeLater()方法
你可以从任何线程调用invokeLater()方法以请求事件派发线程运行特定代码。你必须把要运行的代码放到一个Runnable对象的run() 方法中,并将此Runnable对象设为invokeLater()的参数。invokeLater()方法会立即返回,不等待事件派发线程执行指定代码。这是一个使用invokeLater()方法的例子:
Runnable doWorkRunnable = new Runnable()
{
public void run()
{
doWork();
}
};
SwingUtilities.invokeLater(doWorkRunnable);
使用invokeAndWait()方法
invokeAndWait()方法和invokeLater()方法很相似,除了invokeAndWait()方法会等事件派发线程执行了指定代码才返回。在可能的情况下,你应该尽量用invokeLater()来代替invokeAndWait()。如果你真的要使用invokeAndWait (),请确保调用invokeAndWait()的线程不会在调用期间持有任何其他线程可能需要的锁。
这是一个使用invokeAndWait()的例子:
void showHelloThereDialog() throws Exception
{
Runnable showModalDialog = new Runnable()
{
public void run()
{
JOptionPane.showMessageDialog( myMainFrame, "Hello There");
}
};
SwingUtilities.invokeAndWait (showModalDialog);
}
类似地,假设一个线程需要对GUI的状态进行存取,比如文本域的内容,它的代码可能类似这样:
void printTextField()
throws Exception {
final String[] myStrings = new String[2];
Runnable getTextFieldText = new Runnable() {
public void run() {
myStrings[0] = textField0.getText();
myStrings[1] = textField1.getText();
}
};
SwingUtilities.invokeAndWait (getTextFieldText);
System.out.println(myStrings[0] + " " + myStrings[1]);}
分享到:
相关推荐
在Java开发中,Swing库是用来构建图形用户界面(GUI)的工具包,而多线程则是提升程序并发性能和响应能力的关键技术。Swing虽然是Java语言的一部分,但它设计为单线程模型,主要是为了简化GUI编程并避免复杂的同步...
总的来说,"java多线程+Socket+Swing做的局域网聊天程序"是一个综合性的项目,涵盖了Java基础、网络编程以及GUI设计等多个方面,对于学习和理解Java应用开发具有很高的实践价值。通过这样的项目,开发者可以提升对...
标题中的“JAVA+多线程+swing和awt技术+ 飞机大战+学习Java者”揭示了这个压缩包包含的内容是关于Java编程的,特别是涉及到多线程和图形用户界面(GUI)开发,使用了Java的Swing和AWT库来实现一个“飞机大战”的游戏...
Java雷电游戏是一款基于...通过深入研究和实践Java雷电游戏的源代码,开发者不仅能提升在SWING上的技能,还能掌握Java多线程、事件处理、文件I/O、性能优化以及错误处理等多个核心概念,这对提升Java开发能力大有裨益。
在本项目"基于Java Swing的多线程电梯调度模拟"中,我们主要探讨的是如何利用Java的多线程特性来实现一个复杂的系统——电梯调度。这个任务是在操作系统课程中的一个典型作业,它要求开发者模拟真实世界中的电梯运行...
Java Swing 是Java GUI(图形用户界面)开发的一个库,它基于Java AWT(抽象窗口工具包)并提供了更丰富的组件和事件处理能力。在“JAVASWING多线程产生随机球”的项目中,开发者利用Swing创建了一个互动的应用程序...
在JavaSwing中,开发人员通常会使用JFrame作为主窗口,JButton用于玩家输入(猜拳选择),JLabel或JOptionPane来显示游戏结果和提示信息,可能还会使用JOptionPane来进行对话框交互,比如询问玩家人数或者显示游戏...
在Java Swing中实现多线程下载,我们需要利用Java的并发特性。多线程允许我们同时处理多个任务,对于下载来说,这意味着我们可以将一个大文件分割成多个小部分,然后在不同的线程中并行下载这些部分。这极大地提高了...
Java Swing 是Java GUI(图形用户界面)开发的一个重要库,它是Java Foundation Classes (JFC)的一部分,用于构建桌面应用程序。在这个“java多线程聊天室”项目中,开发者使用Swing构建了一个支持多用户交流的界面...
在这个项目中,开发者利用Java的多线程能力来处理并发用户交互,Socket用于网络通信,而Swing则提供了图形用户界面(GUI)。 首先,我们来看Java的多线程。在多线程环境下,程序可以同时执行多个任务,这对于聊天...
总的来说,这个"《java+swing图形界面开发与案例详解》源代码"资源提供了全面的学习材料,涵盖了从基础组件使用、事件处理、数据展示到高级主题如线程管理和UI定制。通过深入研究这些源代码,开发者不仅能掌握Java ...
它展示了如何与非线程安全的类共同工作,并特别关注于Swing的线程问题。新增加的一章介绍了如何为多处理器机器编写并行代码。 简而言之,本书的新版涉及了有关线程的方方面面,从最简单的动画applet到最复杂的应用...
总的来说,Java Swing聊天室项目结合了GUI设计、事件处理、网络编程和多线程技术,是一个全面展示Java桌面应用开发能力的好例子。通过学习和分析这样的项目,开发者不仅可以提升Swing应用的构建技巧,还能加深对Java...
Swing本身也提供了一些与线程交互的工具,如SwingUtilities的invokeLater()和invokeAndWait()方法,它们用于在EDT上安全地执行代码。例如,当飞机击中目标,分数更新的操作就需要通过invokeLater()方法添加到EDT的...
在Java编程中,多线程是一项关键特性,它允许程序同时执行多个任务,提升系统效率。在处理耗时操作如大文件下载、数据处理或网络请求时,展示进度条能够提供用户友好的交互体验,让使用者了解任务的完成状态。本主题...
通过以上步骤,我们可以在Java Swing中实现一个逼真的下雪效果,同时保持UI的响应性和线程的安全性。这种技能对于开发互动性强、视觉效果丰富的应用程序至关重要,特别是在游戏开发或模拟环境中。了解如何正确地管理...
9. **多线程**:Swing应用程序通常在Event Dispatch Thread(EDT)中运行,理解并发编程对于避免线程安全问题至关重要。 10. **Swing utilities**:如SwingUtilities.invokeLater()用于确保代码在EDT中执行,以及...
在Java Swing程序中,多线程编程有一个特殊要求,即只能在与Swing相同的线程里对GUI元件进行修改。如果在其他线程中修改元件,可以使用类似于SwingUtilities.invokeLater(new Runnable() {public void run() {jLabel...
12. 多线程:在Swing中安全地使用多线程,避免阻塞UI。 13. 国际化:实现应用的多语言支持。 14. 状态栏和进度条:JStatusBar和JProgressBar的使用。 15. 图像处理:加载、显示和处理图像的方法。 16. Swing小部件:...