使用SwingWorker异步加载JTree

 

SwingWorker是Java SE 6.0新加入的一个工具包,利用它可以使长时间运行并更新用户界面的任务大大简化。本文以一个异步加载JTree的demo演示了SwingWorker的基本功能。

 

环境准备

 

软件或资源 要求的版本
Java Development Kit (JDK) 6.0(5.0需要自行加入swingworker到classpath)
NetBeans IDE 6.5
Java DB 10.4.1.3 - (648739)

 

运行demo

  1. 从附件中下载asynch_load_tree.zip到本地并解压。
  2. NetBeans主菜单依次选择“文件”——“打开项目”。
  3. 在弹出的“打开项目”对话框中选择刚才解压之后的asynch_load_tree目录,点击“打开项目”按钮。
  4. 在“文件”视图中找到asynch_load_tree项目下的init_schema_and_data.sql,按照下图步骤操作。

     
  5. 在“项目”视图中找到asynch_load_tree项目,在上下文菜单中选择“运行”项。
  6. 按照下图操作,看一下在EDT 中执行耗时阻塞任务的严重后果。

     
     

EDT是啥米呀

 

在Java中,键盘输入、鼠标点击或者应用程序本身产生的请求会被封装成一个事件,放进一个事件队列中,java.awt.EventQueue对象负责从这个队列中取出事件并派发它们。而EventQueue的派发有一个单独的线程管理,这个线程叫做事件派发线程(Event Dispatch Thread),也就是EDT。此外,Swing的绘制请求也是通过EDT来派发的。

 

根据以上这两个事实,我们就可以解释刚才demo中发生的事情。当checkbox被选中之后,再双击根结点,触发了以下的程序:

 

public void treeExpanded(TreeExpansionEvent event) {
    long handleTime = readHugeData();
    label.setText("处理时间为" + handleTime);
}

事实上,在demo中是用Thread.sleep()方法来模拟readHugeData这个从数据库中读取大量记录这个阻塞方法。那么在这种情况下,EDT在等待数据库的返回期间是不能接收任何事件(比如键盘输入),也不能处理任何组件的绘制。

 

解决的方法是什么呢?很容易想到,把耗时的方法从EDT中剥离出来,放到单独的工作线程中执行。例如下面这样:

 

public void treeExpanded(TreeExpansionEvent event) {
    new Thread(new Runnable() {
        public void run() {
            long handleTime = readHugeData();
            label.setText("处理时间为" + handleTime);
        }
    }).start();
}

很遗憾,这段代码违反了Swing的单线程原则:即没有在EDT上,而是在工作线程里修改这个label的状态。根据SUN的说法,在测试时可能没问题,但死锁可能随时出现,而且多半发生在给老板演示的时候:>

 

在SwingWorker出现之前的解决方法是javax.swing.SwingUtilities类的invokeLater方法。在上面的代码片断中,只需将更新label状态的工作封装成一个Runnable的示例,然后作为参数传递给invokeLater()就好了:

 

public void treeExpanded(TreeExpansionEvent event) {
    new Thread(new Runnable() {
        public void run() {
            final long handleTime = readHugeData();
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {       
                    label.setText("处理时间为" + handleTime);
                }
            });
        }
    }).start();
}

这样就可以保证demo可以平稳和正确地运行了,但是,要创建好多匿名的Runnable,导致代码难于阅读和维护。为了简化与EDT的交互,SwingWorker登场了。


SwingWorker入门

 

这个demo中涉及的只是SwingWorker最基础的东东,所以不必担心。看看如何使用把:

 

public void treeExpanded(TreeExpansionEvent event) {
    new SwingWorker<Long, Void>() {
        @Override
        protected Long doInBackground() {
	     return readHugeData();
        }
        @Override
        protected void done() {
            label.setText("处理时间为" + get());
        }
    }.execute();
}

 

我们把耗时阻塞任务放到doInBackground()方法中,更新组件状态的任务放到done()方法中。当任务完成后,SwingWorder自动调用EDT中的done()方法。此外在done()方法中,可以调用get()方法获取doInBackground()方法的返回值,在demo中这个返回值是任务处理时长。好了,SwingWorker就讲到这里了。什么,那个new SwingWorker<Long, Void>()中第二个泛型类型Void是啥意思?请参考Improve Application Performance With SwingWorker in Java SE 6 这篇牛文。

 

demo的其他细节

 

关于如何自定义TreeNode类、怎样初始化JTree、异步读取所有子结点等问题。还是请看代码把,人家很用心写了注释的说。。。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面我来详细说明一下如何使用SwingWorker来实现动态。 首先,我们需要创建一个SwingWorker的子类,例如: ``` public class MySwingWorker extends SwingWorker<Void, Void> { @Override protected Void doInBackground() throws Exception { // 长时间运行的任务 return null; } @Override protected void done() { // 完成任务后更新UI界面 } } ``` 在上面的代码中,我们定义了一个MySwingWorker类,它继承自SwingWorker类。在doInBackground()方法中,我们可以执行长时间运行的任务,例如数据、解析文件等。在done()方法中,我们可以完成任务后更新UI界面,例如添组件、更新数据等。 接下来,我们可以在UI界面中创建MySwingWorker实例,并调用execute()方法启动后台任务,例如: ``` MySwingWorker worker = new MySwingWorker(); worker.execute(); ``` 在MySwingWorker实例的doInBackground()方法中,我们可以使用SwingUtilities.invokeLater()方法来更新UI界面,例如: ``` @Override protected Void doInBackground() throws Exception { // 长时间运行的任务 SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // 更新UI界面 } }); return null; } ``` 在SwingUtilities.invokeLater()方法中,我们可以更新UI界面,例如显示进度条、更新文本等。 最后,在MySwingWorker实例的done()方法中,我们可以完成任务后更新UI界面,例如: ``` @Override protected void done() { // 完成任务后更新UI界面 JFrame frame = new JFrame("动态"); frame.setSize(300, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } ``` 在上面的代码中,我们创建了一个JFrame窗口,并设置了标题、大小、关闭操作等属性,然后显示窗口。 通过使用SwingWorker类,我们可以实现在请求时动态窗口,同时保持用户界面的响应性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值