虽然各个操作系统之间的线程机制是不一样的
但是大致是相同的
当用户使用GUI程序时
如果点鼠标或按下键盘上的键等时
操作系统会产生对应的GUI事件
它来决定哪个窗口或程序来接受每一个事件并且放到程序的事件队列中
任何GUI程序的底层结构就是一个事件循环程序首先初始化事件循环并开始循环这个循环会从事件队列依次接收GUI事件并一一做出相应的反应程序应该对事件做出快速的反应使程序一直对用户有响应举个例子用户点了一下程序里的一个按钮结果程序就没反应了那么这个程序应该算是一个失败的程序吧
如果某个UI事件引发了某个需要长时间的事务那么应该把它放到一个另外的单独的线程中这样程序的那个事件循环就能够马上回来响应用户的下一个操作线程是非常复杂的一个主题如果处理的不好很容易造成死锁等很糟糕的情况
还好eclipse为我们开发插件提供了一个方便的UI线程包大大的简化了很多底层复杂的东西先看看几个简单的概念
SWT UI线程
SWT用的是操作系统直接支持的线程模式程序会在主程序里运行一个时间循环并依次在这个线程里响应事件看下面这段代码UI线程就是创建Display的那个线程
public static void main (String [] args) {
Display display = new Display ();
Shell shell = new Shell (display);
shellopen ();
// 开始事件循环
// 关掉窗口后
while (!shellisDisposed ()) {
if (!displayreadAndDispatch ())
displaysleep ();
}
displaydispose ();
}
简单的小程序里一个UI线程就能够满足需要了但如果是长时间的操作你就最好不要用UI线程来做这些事可以交给Job去做它其实就是另外启动的线程也就是等会我要说的非UI线程
Job
Job类由reruntime插件提供它能够让客户程序员轻松的在另外的线程中执行代码看一个小例子
Job job = new Job(My First Job) {
protected IStatus run(IProgressMonitor monitor) {
Systemoutprintln(Hello World (from a background job));
return StatusOK_STATUS;
}
};
jobsetPriority(JobSHORT);
jobschedule(); // start as soon as possible
Job的默认优先级是JobLong这里例子中的优先级要比它高只要调用Job#schedule()它就会尽快在另外的线程中运行run()中的代码再看一个小例子:
final Job job = new Job(Long Running Job) {
protected IStatus run(IProgressMonitor monitor) {
try {
while(hasMoreWorkToDo()) {
// do some work
//
if (monitorisCanceled()) return StatusCANCEL_STATUS;
}
return StatusOK_STATUS;
} finally {
schedule(); // start again in an hour
}
}
};
jobaddJobChangeListener(new JobChangeAdapter() {
public void done(IJobChangeEvent event) {
if (eventgetResult()isOK())
postMessage(Job completed successfully);
else
postError(Job did not complete successfully);
}
});
jobsetSystem(true);
jobschedule(); // start as soon as possible
monitor是一个进度显示条它会在运行job时自动显示如果任务成功运行完成返回StatusOK_STATUS如果中途被用户在进度显示条那里中断就返回StatusCANCEL_STATUS上面schedule();它是让job每过小时就自动运行Job又一个非常强大的功能然后后面是可以给job添加监听器
jobsetSystem(true);这一句是把这个job设置为系统级别的如果调用setUser(true)那么就被定义为用户级别的用户级别和默认级别的job
在运行时会以UI形式反映出来如果是用户job那么会弹出一个进度显示窗口能让用户选择在后台里运行下图是一个job自动运行时的效果:
再介绍job常常用到的一个方法Job#join()系统调用到某个job调用它的run()方法再看下面这个例子:
class TrivialJob extends Job {
public TrivialJob() {
super(Trivial Job);
}
public IStatus run(IProgressMonitor monitor) {
Systemoutprintln(This is a job);
return StatusOK_STATUS;
}
}
job的创建和计划如下所示:
TrivialJob job = new TrivialJob();
Systemoutprintln(About to schedule a job);
jobschedule();
Systemoutprintln(Finished scheduling a job);
他们的执行是和时间没关系的输出可能如下:
About to schedule a job
This is a job
Finished scheduling a job
也可能是:
About to schedule a job
Finished scheduling a job
This is a job
如果希望某个job运行完成后在继续时可以使用join()方法join()会一直阻塞到该job运行完
例子:
TrivialJob job = new TrivialJob();
Systemoutprintln(About to schedule a job);
jobschedule();
jobjoin();
if (jobgetResult()isOk())
Systemoutprintln(Job completed with success);
else
Systemoutprintln(Job did not complete successfully);
上面的代码执行后输出应该就是这样:
About to schedule a job
This is a job
Job completed with success
Job的功能是很强大的还有很多功能我以后会介绍也可以查阅官方帮助文档这里先把几个常用的问题解决掉参见:
?topic=/orgeclipseplatformdocisv/guide/
如果在Job中加上改变UI的代码就会失败原因如下:
如果是在非UI线程中调用UISWT就会抛出一个SWTException要在一个非UI线程改变UI的话有几种技术:
第一种用:
Display#syncExec(Runnable)或
Diaplay#asyncExec(Runnable)
第二种:
已经开发了另外一种Job就是UIJob可以直接在它里面运行改变UI的代码其实它就是在SWT的asyncExec()方法里运行的所有继承UIJob的类应该覆写runInUIThread方法而不是run方法
关于进度显示
在Jface中:
orgeclipsejfaceoperations包定义了一些接口用来在进度条下运行长时间的任务可以参见:
?topic=/orgeclipseplatformdocisv/guide/
在eclipse插件和RCP开发中:
用户级别的job是互操作性最强的它不仅能够让用户用Cancel键取消job而且可以在Detail中展示具体情况但是注意:
Detail只会在下面两种方法中出现:
IProgressService#busyCursorWhile或
IProgressService#runInUI
)IProgressService#busyCursorWhile的用法例子:
注意这里的run()中做些和UI无关的事
IProgressService progressService = PlatformUIgetWorkbench()getProgressService();
progressServicebusyCursorWhile(new IRunnableWithProgress(){
public void run(IProgressMonitor monitor) {
//do nonUI work
}
});
效果:
) IProgressService#runInUI的用法例子:
注意这里的run()中可以做些和UI有关的事
progressServicerunInUI(
PlatformUIgetWorkbench()getProgressService()
new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) {
//do UI work
}
}
PlatformgetWorkspace()getRoot());
效果:
这里最后一个参数可以是null或者是这个操作的规则在这里我们是设定运行这个UI操作时锁定工作台
更加具体的可以参见:
?topic=/orgeclipseplatformdocisv/guide/
另外有少数时候我们不想弹出一个进度条窗口而是只在最底下的状态栏显示就可以了很简单写自己的Job类时在构造方法里加上一句:
setUser(false);就可以了