要理解UI线程,先要了解一下“消息循环”这个概念。链接是百度百科上的条目,简单地说,操作系统把用户界面上的每个操作都转化成为对应的消息,加入消息队列。然后把消息转发给对应的应用程序(一般来说,就是活动窗口),应用程序根据自己的逻辑处理这些消息。 如果应用程序处理某个消息事件的时候,用了很长的时间,这时候后续的消息无法及时得到处理,就会造成应用程序没有响应,也就是常说的“假死”状态。 所以,应用程序如果处理某个事件需要较长的时间,需要把这个操作放到一个另外的线程中进行处理。 下面再回顾一下前面的简单的SWT程序的结构:

public static void main(String[] args) {
Display display = new Display ();
Shell shell = new Shell (display);
......
shell.open ();
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
display.dispose ();
}

while循环一段就是处理消息循环的开始,也就是说,一个SWT程序的主线程,就是对应的所谓的UI线程。

程序中什么地方是UI线程什么地方是非UI线程

  1. 主线程是UI线程
  2. 监听器方法中是UI线程 比如下面这段小程序:
Label label = new Label (shell, SWT.NONE);
label.setText ("Enter your name:");
Text text = new Text (shell, SWT.BORDER);
text.setLayoutData (new RowData (100, SWT.DEFAULT));
Button ok = new Button (shell, SWT.PUSH);
ok.setText ("OK");
ok.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
while(true) {
System.out.println(1);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
});

程序中模拟在点击按钮后,执行一段费时的操作,运行可以看到,程序处于无响应状态:

避免无响应

那么,如何避免程序进入无响应状态呢? 其实很简单,不要在UI线程中执行长时间的操作,如果必需要执行费时操作,就在另外的线程中执行:

ok.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
new Thread() {
public void run() {
while(true) {
System.out.println(1);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}.start();
}
});

这样再次执行,可以看到点击按钮后,程序不再会进入无响应状态。

非UI线程访问UI

所以对控件的操作都必需在UI线程中进行,否则会抛出线程访问错误,还是以上面代码为例,我们现在改成打印text控件的文本:

ok.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// 此处代码直接在监听器方法中,是UI线程
new Thread() {
public void run() {
// 此处为另外一个单独线程,非UI线程
while(true) {
System.out.println(text.getText());
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}.start();
}
});

运行程序,点击按钮,就会抛出下面的异常:

Exception in thread "Thread-0" org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(SWT.java:4441)
at org.eclipse.swt.SWT.error(SWT.java:4356)
at org.eclipse.swt.SWT.error(SWT.java:4327)
at org.eclipse.swt.widgets.Widget.error(Widget.java:476)
at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:367)
at org.eclipse.swt.widgets.Text.getText(Text.java:1350)
at test.Snippet108$1$1.run(Snippet108.java:24)

对于这种在非UI线程访问UI的情况,需要用Display类的syncExec(Runnable)或asyncExec(Runnable)两个方法来执行:

ok.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// 此处代码直接在监听器方法中,是UI线程
new Thread() {
public void run() {
// 此处为另外一个单独线程,非UI线程
while(true) {
// 非UI线程访问UI
display.syncExec(new Runnable() {
@Override
public void run() {
// 这段代码实际上会被放在UI线程中执行
System.out.println(text.getText());
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}.start();
}
});

注意上面的注释说明,syncExec(runnable)方法的参数runnable对象实际上是会被放在UI线程中执行的,所以要注意,不要把Tread.sleep()放到这个runnable里,否则同样会导致界面无响应。

syncExec和asyncExec方法的区别就是这两个方法一个会等待runnable执行完才返回,asyncExec方法则是立即返回,UI线程会在有空闲的时候去执行runnable。

那么,是否能够用asyncExec方法执行,同时把上面的sleep放到runnable中呢? 答案依然是不能,原因前面已经提到了,因为runnable实际上是会放到UI线程中执行的,如果这个runnable是非常耗时的,同样会让界面不断陷入每次1秒的无响应状态中, 也相当于新开一个线程执行耗时操作的目的就没有达到了。

关于 SWT 的UI线程和非UI线程的更多相关文章

  1. Android UI线程和非UI线程

    Android UI线程和非UI线程 UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这 ...

  2. android脚步---如何看log之程序停止运行,和UI线程和非UI线程之间切换

    经常运行eclipse时,烧到手机出现,“停止运行”,这时候得通过logcat查log了.一般这种情况属于FATAL EXCEPTION,所以检索FATAL 或者 EXCEPTION,然后往下看几行 ...

  3. Java中的守护线程和非守护线程(转载)

    <什么是守护线程,什么是非守护线程> Java有两种Thread:"守护线程Daemon"(守护线程)与"用户线程User"(非守护线程). 用户线 ...

  4. 从头认识java-18.2 主要的线程机制(5)-守护线程与非守护线程

    这一章节我们来讨论一下守护线程与非守护线程. 1.什么是守护线程?什么是非守护线程? 非守护线程:Java虚拟机在它全部非守护线程已经离开后自己主动离开. 守护线程:守护线程则是用来服务用户线程的,假 ...

  5. Android线程---UI线程和非UI线程之间通信

        近期自学到了线程这一块,用了一上午的时间终于搞出来了主.子线程间的相互通信.当主线程sendMessage后,子线程便会调用handleMessage来获取你所发送的Message.我的主线程 ...

  6. java多线程之守护线程与非守护线程

    在java线程中有两种线程,一种是用户线程,其余一种是守护线程. 守护线程具有特殊的含义,比如gc线程.当最后一个非守护线程执行完后,守护线程随着jvm一同结束工作. java中的守护线程需要将Dae ...

  7. java的守护线程与非守护线程

    最近重新研究Java基础知识,发现以前太多知识知识略略带过了,比较说Java的线程机制,在Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) ,(PS:以 ...

  8. [Java基础] java的守护线程与非守护线程

    最近重新研究Java基础知识,发现以前太多知识知识略略带过了,比较说Java的线程机制,在Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) ,(PS:以 ...

  9. Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面

    Android应用的开发过程中需要把繁重的任务(IO,网络连接等)放到其他线程中异步执行,达到不阻塞UI的效果. 下面将由浅入深介绍Android进行异步处理的实现方法和系统底层的实现原理. 本文介绍 ...

随机推荐

  1. HDU 4819 Mosaic 【二维线段树】

    题目大意:给你一个n*n的矩阵,每次找到一个点(x,y)周围l*l的子矩阵中的最大值a和最小值b,将(x,y)更新为(a+b)/2 思路:裸的二维线段树 #include<iostream> ...

  2. 常州模拟赛d3t1 神在夏至祭降下了神谕

    题目描述 我们村子在过去的400年中,断绝与下界的接触,过着自给自足的生活. 夏至祭是一场迎接祖灵于夏季归来,同时祈求丰收的庆典. 村里的男人会在广场上演出夏之军和冬之军的战争.夏之军会打倒冬之军的大 ...

  3. polya burnside 专题

    polya题目:uva 11077 Find the Permutationsuva 10294 Arif in DhakaLA 3641 Leonardo's Notebookuva 11077 F ...

  4. 洛谷 [P3224] 永无乡

    Treap 的合并 首先感谢 @Capella 的DeBug 其次,这是由一个 & 号引发的血案 注意对于所有修改操作都要 & Treap的合并, 启发式合并,对于每一个节点都 ins ...

  5. Java中获取项目根路径和类加载路径的7种方法

    引言 在web项目开发过程中,可能会经常遇到要获取项目根路径的情况,那接下来我就总结一下,java中获取项目根路径的7种方法,主要是通过thisClass和System,线程和request等方法. ...

  6. 注解@RequestMapping value 用法

    本文引自:https://blog.csdn.net/qq_33811662/article/details/80864784 RequestMapping是一个用来处理请求地址映射的注解,可用于类. ...

  7. activiti实现的请假流程

    直接上图,还是有点复杂的

  8. SolidEdge 如何由装配图快速生成爆炸视图

    建立图纸精灵,组态中选择explode(没有下一步可选了)   点击完成即可绘制爆炸图

  9. window.onresize 事件笔记

     1.浏览器尺寸变化响应事件 : window.onresize = function(){....} 这里须要注意的是,onresize响应事件处理中.获取到的页面尺寸參数是变更后的參数. // ...

  10. 通过Java反射做实体查询

    我们在使用hibernate的时候,查询的时候都会和实体中的一些字段相结合去查询,当然字段少了,还算是比较简单,当字段多了,就不那么容易了,所以就自己写了个方法,根据实体中的字段信息去查询,废话不多说 ...