SWT图形界面开发中多线程的使用

一、场景
开发java桌面应用大都会带有图形界面,带有图形界面的java程序一般都会有专门负责图形界面渲染、用户输入事件的UI线程,比如swing中的awt线程,swt/jface中的swt线程。

这里主要说一下swt中关于界面的多线程。
桌面程序开发过程中,难免遇到耗时很长的任务,如果放到UI线程中处理,会有界面假死的感觉,用户体验很不好。常用的解决办法就是为这个耗时的任务开辟一个新线程,放到后台去处理,保证界面操作的流畅;后台任务完成后,图形界面获得通知,做出相应的响应。但这种方法在SWT中如何实现呢?

二、问题
SWT在设计上不支持多线程,就是说在非UI线程中调用UI对象是不允许的,若要访问UI界面上的对象必须通过UI线程来访问。SWT在设计上保证在一个线程中使用,这是出于线程安全的考虑。大家可以深入研究一下这么做的目的,我的理解是SWT需要手动释放资源,不想swing那样使用AGC,所以不像swing那样支持多线程。

如果我们从非UI线程去操作UI界面会有什么结果呢?运行下面的代试一下:

package designer;

import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;

public class MultiThreadTest extends ApplicationWindow {
Text text;

public MultiThreadTest() {
super(null);
}

public Control createContents(Composite parent) {
Shell shell = getShell();
shell.setText("Where am I?");
text = new Text(shell, SWT.NONE);
text.setText("Hello world");
return parent;
}

public static void main(String[] args) {
MultiThreadTest main = new MultiThreadTest();
main.setBlockOnOpen(true);
Task task = new Task(main);
task.start();
main.open();
Display.getCurrent().dispose();
}
}

class Task extends Thread {
private MultiThreadTest main;

public Task(MultiThreadTest main) {
this.main = main;
}

public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
}
main.text.insert("Task begin...");
for (int i = 0; i < 50; i++) {
main.text.insert("processing " + i + " task...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
}

正常情况下,应该抛出一个运行时错误:
[color=red]Exception in thread "Thread-0" org.eclipse.swt.SWTException: Invalid thread access[/color]

三、解决方法
Display是突破口。Display作为SWT运行时的核心,管理GUI资源,与OS进行通信。
我们必须为更新GUI的任务开辟一个线程,交由UI线程去调用,而让UI线程调用其他任务的方法是通过Display对象:
[quote="http://hi.baidu.com/gridrender/blog/item/447c7d181121950135fa410d.html"]
Display对象中负责调用其他线程的方法有以下3种:

● asyncExec(Runnable runnable):异步启动新的线程。所谓异步就是,UI线程不会等待runnable对象执行结束后再继续进行,就是说UI线程可以和runnable对象所在的线程同时运行。
● syncExec(Runnable runnable):同步启动新的线程。所谓同步就是,UI线程会等待runnable对象执行结束后才会继续进行,当runnable对象是耗时大的线程时,尽量不要采用此种方式。另外,对于该种方式创建的线程可通过getSyncThread()方法获得线程对象。
● timerExec(int milliseconds,Runnable runnable):指定一段时间再启动新的线程。用此方法创建的线程,将会在指定的时间后再启动线程。当然用此方法创建的线程启动后,与UI线程是异步的。如果指定的时间为负数,将不会按时启动线程。

另外Display对象中,与UI线程相关的方法如下所示:
● 获得当前的UI线程对象的方法:getThread(),返回Thread对象。
● 使UI线程处于休眠状态:sleep()。
● 唤醒UI线程:wake()。
[/quote]
3.1获得Display对象
非UI线程可以通过Display.getDefault()获得。如果之前全局中存在Display对象则直接获得;如果之前没有Display对象存在(如SWT之前没调用Display.getDefault()方法,JFace的ApplicationWindow还没有open()),则该线程编程UI线程。
3.2调用策略
暂把为处理耗时任务而开辟的线程叫做任务线程。有两种策略来处理:一是在触发该任务线程时由Display对象启动任务线程(但是这样相当于耗时的任务在UI线程中运行了,界面还是会假死。);二是任务线程中需要更新界面时,通过Display对象启动包含更新动作的新线程。


四、更好的方法
利用SWT的Action和Contribution。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值