- 浏览: 697788 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
yzs5273:
没什么用。都试过了
WIN7下CS不能全屏的解决方法 -
di1984HIT:
不错,学习了
读取本地计算机中的安装程序列表 -
ffedu:
[flash=200,200][url][img][list] ...
linux/unix中如何用find命令详解,非常详细的介绍,比man find强100倍(转) -
lintghi:
...
Log4j使用相对路径指定log文件及使用总结 -
nick.s.ni:
唉,Java中引用的包没有介绍啊,如果数据库用UTF-8的格式 ...
Oracle 中Java 对象与PL/SQL类型的映射及使用(转)
原文:http://today.java.net/pub/a/today/2003/10/24/swing.html?page=1 不正确的Swing线程是运行缓慢、无响应和不稳定的Swing应用的主要原因之一。这是许多原因造成的,从开发人员对Swing单线程模型的误解,到保证正确的线程执行的困难。即使对Swing线程进行了很多努力,应用线程逻辑也是很难理解和维护的。本文阐述了如何在开发Swing应用中使用事件驱动编程,以大大简化开发、维护,并提供高灵活性。背景
既然我们是要简化Swing应用的线程,首先让我们来看看Swing线程是怎么工作的,为什么它是必须的。Swing API是围绕单线程模型设计的。这意味着Swing组件必须总是通过同一个线程来修改和操纵。为什么采用单线程模型,这有很多原因,包括开发成本和同步Swing的复杂性--这都会造成一个迟钝的API。为了达到单线程模型,有一个专门的线程用于和Swing组件交互。这个线程就是大家熟知的Swing线程,AWT(有时也发音为“ought”)线程,或者事件分派线程。在本文的下面的部分,我选用Swing线程的叫法。
既然Swing线程是和Swing组件进行交互的唯一的线程,它就被赋予了很多责任。所有的绘制和图形,鼠标事件,组件事件,按钮事件,和所有其它事件都发生在Swing线程。因为Swing线程的工作已经非常沉重了,当太多其它工作在Swing线程中进行处理时就会发生问题。会引起这个问题的最常见的位置是在非Swing处理的地方,像发生在一个事件监听器方法中,比如JButton的ActionListener,的数据库查找。既然ActionListener的actionPerformed()方法自动在Swing线程中执行,那么,数据库查找也将在Swing线程中执行。这将占用了Swing的工作,阻止它处理它的其它任务--像绘制,响应鼠标移动,处理按钮事件,和应用的缩放。用户以为应用死掉了,但实际上并不是这样。在适当的线程中执行代码对确保系统正常地执行非常重要。
既然我们已经看到了在适当的线程中执行Swing应用的代码是多么重要,现在让我们如何实现这些线程。我们看看将代码放入和移出Swing线程的标准机制。在讲述过程中,我将突出几个和标准机制有关的问题和难点。正如我们看到的,大部分的问题都来自于企图在异步的Swing线程模型上实现同步的代码模型。从那儿,我们将看到如何修改我们的例子到事件驱动--移植整个方式到异步模型。通用Swing线程解决方案
让我们以一个最常用的Swing线程错误开始。我们将企图使用标准的技术来修正这个问题。在这个过程中,我们将看到实现正确的Swing线程的复杂性和常见困难。并且,注意在修正这个Swing线程问题中,许多中间的例子也是不能工作的。在例子中,我在代码失败的地方以//broken开头标出。好了,现在,让我们进入我们的例子吧。
假设我们在执行图书查找。我们有一个简单的用户界面,包括一个查找文本域,一个查找按钮,和一个输出的文本区域。这个接口如图1所示。不要批评我的UI设计,这个确实很丑陋,我承认。
图 1. 基本查询用户界面
用户输入书的标题,作者或者其它条件,然后显示一个结果的列表。下面的代码例子演示了按钮的ActionListener在同一个线程中调用lookup()方法。在这些例子中,我使用了thread.sleep()休眠5秒来作为一个占位的外部查找。线程休眠的结果等同于一个耗时5秒的同步的服务器调用。
private void searchButton_actionPerformed() {
outputTA.setText("Searching for: " +
searchTF.getText());
//Broken!! Too much work in the Swing thread
String[] results = lookup(searchTF.getText());
outputTA.setText("");
for (int i = 0; i < results.length; i++) {
String result = results[i];
outputTA.setText(outputTA.getText() +
'\n' + result);
}
}
如果你运行这段代码(完整的代码可以在这儿下载),你会立即发现存在一些问题。图2显示了查找运行中的一个屏幕截图。
图 2. 在Swing线程中进行查找
注意Go按钮看起来是被按下了。这是因为actionPerformed方法通知了按钮绘制为非按下外观,但是还没有返回。你也会发现要查找的字串“abcde”并没有出现在文本区域中。searchButton_actionPerformed的第1行代码将文本区域设置为要查找的字串。但是,注意Swing重画并不是立即执行的。而是把重画请求放置到Swing事件队列中等待Swing线程处理。但是这儿,我们因查找处理占用了Swing线程,所以,它还不能马上进行重画。
要修正这些问题,让我们把查找操作移入非Swing线程中。我们第一个想到的就是让整个方法在一个新的线程中执行。这样作的问题是Swing组件,本例中的文本区域,只能从Swing线程中进行编辑。下面是修改后的searchButton_actionPerformed方法:
private void searchButton_actionPerformed() {
outputTA.setText("Searching for: " +
searchTF.getText());
//the String[][] is used to allow access to
// setting the results from an inner class
final String[][] results = new String[1][1];
new Thread(){
public void run() {
results[0] = lookup(searchTF.getText());
}
}.start();
outputTA.setText("");
for (int i = 0; i < results[0].length; i++) {
String result = results[0][i];
outputTA.setText(outputTA.getText() +
'\n' + result);
}
}
这种方法有很多问题。注意final String[][] 。这是一个处理匿名内部类和作用域的不得已的替代。基本上,在匿名内部类中使用的,但在外部环绕类作用域中定义的任何变量都需要定义为final。你可以通过创建一个数组来持有变量解决这个问题。这样的话,你可以创建数组为final的,修改数组中的元素,而不是数组的引用自身。既然我们已经解决这个问题,让我们进入真正的问题所在吧。图3显示了这段代码运行时发生的情况:
图 3. 在Swing线程外部进行查找
界面显示了一个null,因为显示代码在查找代码完成前被处理了。这是因为一旦新的线程启动了,代码块继续执行,而不是等待线程执行完毕。这是那些奇怪的并发代码块中的一个,下面将把它编写到一个方法中使其能够真正执行。
在SwingUtilities类中有两个方法可以帮助我们解决这些问题:invokerLater()和invokeAndWait()。每一个方法都以一个Runnable作为参数,并在Swing线程中执行它。invokeAndWait()方法阻塞直到Runnnable执行完毕;invokeLater()异步地执行Runnable。invokeAndWait()一般不赞成使用,因为它可能导致严重的线程死锁,对你的应用造成严重的破坏。所以,让我们把它放置一边,使用invokeLater()方法。
要修正最后一个变量变量scooping和执行顺序的问题,我们必须将文本区域的getText()和setText()方法调用移入一个Runnable,只有在查询结果返回后再执行它,并且在Swing线程中执行。我们可以这样作,创建一个匿名Runnable传递给invokeLater(),包括在新线程的Runnable后的文本区域操作。这保证了Swing代码不会在查找结束之前执行。下面是修正后的代码:
private void searchButton_actionPerformed() {
outputTA.setText("Searching for: " +
searchTF.getText());
final String[][] results = new String[1][1];
new Thread() {
public void run() {
//get results.
results[0] = lookup(searchTF.getText());
// send runnable to the Swing thread
// the runnable is queued after the
// results are returned
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
// Now we're in the Swing thread
outputTA.setText("");
for (int i = 0;
i < results[0].length;
i++) {
String result = results[0][i];
outputTA.setText(
outputTA.getText() +
'\n' + result);
}
}
}
);
}
}.start();
}
这可以工作,但是这样做令人非常头痛。我们不得不对通过匿名线程执行的顺序,我们还不得不处理困难的scooping问题。问题并不少见,并且,这只是一个非常简单的例子,我们已经遇到了作用域,变量传递,和执行顺序等一系列问题。相像一个更复杂的问题,包含了几层嵌套,共享的引用和指定的执行顺序。这种方法很快就失控了。问题
我们在企图强制通过异步模型进行同步执行--企图将一个方形的螺栓放到一个圆形的空中。只有我们尝试这样做,我们就会不断地遭遇这些问题。从我的经验,可以告诉你这些代码很难阅读,很难维护,并且易于出错。
这看起来是一个常见的问题,所以一定有标准的方式来解决,对吗?出现了一些框架用于管理Swing的复杂性,所以让我们来快速预览一下它们可以做什么。
一个可以得到的解决方案是Foxtrot,一个由Biorn Steedom写的框架,可以在SourceForge上获取。它使用一个叫做Worker的对象来控制非Swing任务在非Swing线程中的执行,阻塞直到非Swing任务执行完毕。它简化了Swing线程,允许你编写同步代码,并在Swing线程和非Swing线程直接切换。下面是来自它的站点的一个例子:
public void actionPerformed(ActionEvent e)
{
button.setText("Sleeping...");
String text = null;
try
{
text = (String)Worker.post(new Task()
{
public Object run() throws Exception
{
Thread.sleep(10000);
return "Slept !";
}
});
}
catch (Exception x) ...
button.setText(text);
somethingElse();
}
注意它是如何解决上面的那些问题的。我们能够非常容易地在Swing线程中传入传出变量。并且,代码块看起来也很正确--先编写的先执行。但是仍然有一些问题障碍阻止使用从准同步异步解决方案。Foxtrot中的一个问题是异常管理。使用Foxtrot,每次调用Worker必须捕获Exception。这是将执行代理给Worker来解决同步对异步问题的一个产物。
同样以非常相似的方式,我此前也创建了一个框架,我称它为链接运行引擎(Chained Runnable Engine) ,同样也遭受来自类似同步对异步问题的困扰。使用这个框架,你将创建一个将被引擎执行的Runnable的集合。每一个Runnable都有一个指示器告诉引擎是否应该在Swing线程或者另外的线程中执行。引擎也保证Runnable以正确的顺序执行。所以Runnable #2将不会放入队列直到Runnable #1执行完毕。并且,它支持变量以HashMap的形式从Runnable到Runnable传递。
表面上,它看起来解决了我们的主要问题。但是当你深入进去后,同样的问题又冒出来了。本质上,我们并没有改变上面描述的任何东西--我们只是将复杂性隐藏在引擎的后面。因为指数级增长的Runnable而使代码编写将变得非常枯燥,也很复杂,并且这些Runnable常常相互耦合。Runnable之间的非类型的HashMap变量传递变得难于管理。问题的列表还有很多。
在编写这个框架之后,我意识到这需要一个完全不同的解决方案。这让我重新审视了问题,看别人是怎么解决类似的问题的,并深入的研究了Swing的源代码。
发表评论
-
Transfer
2017-06-29 23:03 0Find connections count: ... -
Discover the Mystery of Metaspace
2017-06-23 16:47 0The JDK 8 HotSpot JVM is now u ... -
Command Line JMX Client
2014-12-29 13:12 2595Command Line Parser: GNUComman ... -
Something about JVM class loading and initialization
2014-05-09 10:04 1032Class loading stages: Loadin ... -
When a class is loaded and initialized in JVM - Java
2014-05-08 19:09 988from: http://javarevisited.blo ... -
【深入Java虚拟机】之四:类加载机制
2014-05-08 15:12 899转载请注明出处:http://blog.csdn.net/n ... -
Java Reflection - Dynamic Class Loading and Reloading
2014-05-08 12:04 942From: http://tutorials.jenkov. ... -
Java 类加载与初始化
2014-02-19 19:12 810转载自:http://www.cnblogs.c ... -
javax.management.StandardMBean: When and Why. (Reposted)
2013-12-26 15:34 1084Q: When is a Standard MBean no ... -
JVM调优的"标准参数"的各种陷阱(转)
2013-11-11 19:55 2051From: http://hllvm.group.itey ... -
Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning
2013-11-11 11:05 1018(From: http://www.oracle.com/ ... -
An article about TLAB
2013-11-11 10:57 756(From: https://blogs.oracle.co ... -
【JVM】HotSpot JVM内存管理和GC策略总结(转)
2013-11-07 23:39 592JVM的相关知识是学习java高级特性必须要去深入学习的。平 ... -
jstat分析VM内存
2013-11-07 16:41 904Jstat 是JDK自带的一个轻量级小工具。全称“Java ... -
java的GridBagLayout网格包布局管理器使用详解 (转)
2013-11-01 16:44 0网格包布局管理是最复 ... -
java动态跟踪分析工具BTrace实现原理
2013-09-01 12:34 1267转自:http://kenwublog.com ... -
Java synchronize用法(转)
2012-11-05 00:20 1207在多个并发线程之间共用资源,就需要进行同步处理。Java虚拟机 ... -
Interview material collection
2012-07-09 23:05 11901. Why can't static methods be ... -
不要重复 DAO!(转)
2011-12-29 22:17 1242使用 Hibernate 和 Spri ... -
JVM 诊断工具(转)
2011-11-25 12:00 17611.jinfo 描述:输出给定 java 进程所有的配置信 ...
相关推荐
Swing线程的深入理解和SwingWorker基础知识介绍 Swing线程是Java程序设计中的一种重要概念,用于处理图形用户界面(GUI)中的线程问题。在本文中,我们将深入探讨Swing线程的理解和SwingWorker基础知识,并通过实例...
Swing API设计为单线程模型,这意味着所有对Swing组件的修改和操作必须在同一个线程中完成,即Swing线程(AWT事件分派线程)。这种设计旨在减少线程同步的复杂性,但同时也引入了挑战,因为不当的线程使用可能导致...
Swing线程基础是Java GUI编程中的重要概念,特别是对于使用Swing库构建桌面应用程序的开发者而言。Swing设计遵循单线程模型,确保UI组件的线程安全性和响应性。以下是Swing线程基础的详细说明: 1. **Swing应用程序...
3. Event Dispatch Thread(事件分发线程):Swing是单线程的,所有对组件的操作必须在EDT上进行,以确保界面的同步更新。 二、多线程技术 1. 用户界面线程:主GUI运行在EDT上,负责处理用户输入和显示UI更新。 2. ...
【Swing线程的最后讨论 -- 利用异步模型】 Swing框架遵循单线程规则,即所有UI组件在同一时间只能被一个线程访问,通常这个线程就是事件派发线程。这一规则是为了避免多线程环境下的竞态条件和同步问题,确保组件的...
Java Swing多线程下载器是一种利用Java Swing库构建的图形用户界面(GUI)应用程序,它具备多线程下载功能,并支持断点续传。这样的工具类似于我们熟知的迅雷下载管理器,允许用户同时下载多个文件,提高下载速度,...
Swing 组件是线程不安全的,这意味着只能从事件派发线程(Event Dispatch Thread,简称 EDT)访问将要在屏幕上绘制的 Swing 组件。如果从其他线程访问 Swing 组件,将会导致程序崩溃或出现不可预测的结果。 在上面...
Swing组件默认不是线程安全的,因此必须通过`SwingUtilities.invokeLater()`、`SwingUtilities.invokeAndWait()`或使用`SwingWorker`类来确保所有对Swing组件的操作都在事件分发线程中执行。这些技术的应用可以有效...
在Java Swing中创建动态、交互式的应用,线程管理是一个关键的概念。当我们谈论"下雪效果"时,这意味着在Swing程序中实现一个模拟下雪场景的动画。 首先,要实现下雪效果,我们需要理解Swing的事件处理机制。Swing...
在这个“swing 线程 实现奖项抽取”的项目中,开发者利用Swing来创建一个交互式的抽奖应用,用户可以通过点击按钮触发抽奖过程。这个过程涉及到了多线程的概念,以确保界面的流畅性和数据处理的并发性。 首先,...
Swing线程基础是Java GUI编程中的重要概念,特别是对于使用Swing库构建的应用程序而言。Swing设计遵循单一线程原则,确保图形用户界面(GUI)的稳定性和响应性。以下是Swing线程基础的详细说明: 1. **初始化线程 ...
在“JAVASWING多线程产生随机球”的项目中,开发者利用Swing创建了一个互动的应用程序,用户可以通过鼠标点击在界面上生成一个球体,这个球体会以随机的方向和速度在窗口内移动。下面将详细解释这个项目涉及的知识点...
综上所述,理解并遵循Swing的线程规则是编写高效、响应式的GUI应用的基础。合理利用初始化线程、EDT和任务线程,可以确保用户界面的流畅性和应用程序的稳定性。在实际开发中,应始终关注线程安全和性能优化,确保...
Swing线程机制是Swing库中的关键概念,它确保了所有对Swing组件的操作都在正确的线程——事件分发线程(Event Dispatch Thread,简称EDT)上执行,以避免线程安全问题和界面更新不一致的情况。`SwingUtilities....
在Java开发中,Swing库是用来构建图形用户界面(GUI)的工具包,而多线程则是提升程序并发性能和响应能力的关键技术。Swing虽然是Java语言的一部分,但它设计为单线程模型,主要是为了简化GUI编程并避免复杂的同步...
Swing 线程的深入理解和 SwingWorker 基础知识介绍 Swing 是 Java 中的一种图形用户界面(GUI)工具包,用于创建桌面应用程序。然而,在使用 Swing 时,开发者经常会遇到线程相关的问题。本文将深入了解 Swing 线程...
综上所述,这个“swing界面socket多线程聊天室”项目涵盖了Java GUI设计、网络编程、多线程技术、文件传输以及用户管理等多个关键领域,是学习和实践这些技能的好例子。通过分析和实现这样的项目,开发者可以深入...
在本项目"基于Java Swing的多线程电梯调度模拟"中,我们主要探讨的是如何利用Java的多线程特性来实现一个复杂的系统——电梯调度。这个任务是在操作系统课程中的一个典型作业,它要求开发者模拟真实世界中的电梯运行...