关于 SWT 的UI线程和非UI线程

要理解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秒的无响应状态中, 也相当于新开一个线程执行耗时操作的目的就没有达到了。

时间: 2024-10-04 09:20:44

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

SWT的UI线程和非UI线程

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

Android UI线程和非UI线程

UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的UI组件(components from the Android UI toolkit (components from the android.widget and android.view packages))发生交互. 所以main thread也叫UI thread也即UI线程. 系统不会为

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

经常运行eclipse时,烧到手机出现,“停止运行”,这时候得通过logcat查log了.一般这种情况属于FATAL EXCEPTION,所以检索FATAL 或者 EXCEPTION,然后往下看几行 例子: 11-26 16:18:17.949: E/AndroidRuntime(5363): FATAL EXCEPTION: Thread-19311-26 16:18:17.949: E/AndroidRuntime(5363): Process: com.scme.jiance, PID:

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

这一章节我们来讨论一下守护线程与非守护线程. 1.什么是守护线程?什么是非守护线程? 非守护线程:Java虚拟机在它全部非守护线程已经离开后自己主动离开. 守护线程:守护线程则是用来服务用户线程的,假设没有其它用户线程在运行,那么就没有可服务对象,也就没有理由继续下去. 2.同样点 大家都是线程.事实上能够互相切换 3.不同点:退出的时间点 退出的先后顺序: 非守护线程->守护线程->jvm 4.注意点: (1)设置守护线程须要在start之前,否在抛异常 package com.ray.ch

从头认识java-18.2 基本的线程机制(5)-守护线程与非守护线程

这一章节我们来讨论一下守护线程与非守护线程. 1.什么是守护线程?什么是非守护线程? 非守护线程:Java虚拟机在它所有非守护线程已经离开后自动离开. 守护线程:守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去. 2.相同点 大家都是线程,其实可以互相切换 3.不同点:退出的时间点 退出的先后顺序: 非守护线程->守护线程->jvm 4.注意点: (1)设置守护线程需要在start之前,否在抛异常 package com.ray.ch17;

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

<什么是守护线程,什么是非守护线程> Java有两种Thread:“守护线程Daemon”(守护线程)与“用户线程User”(非守护线程). 用户线程:非守护线程包括常规的用户线程或诸如用于处理GUI事件的事件调度线程,Java虚拟机在它所有非守护线程已经离开后自动离开. 守护线程:守护线程则是用来服务用户线程的,比如说GC线程.如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去.(操作系统里面是没有所谓的守护线程的概念,只有守护进程一说,但是Java语言机制是构建在JVM

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

最近重新研究Java基础知识,发现以前太多知识知识略略带过了,比较说Java的线程机制,在Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) ,(PS:以前忽略了). 估计学过Unix开发但是没有细致学习Java的同学们会疑惑了,操作系统里面是没有所谓的守护线程的概念,只有守护进程一说,但是Java语言机制是构 建在JVM的基础之上的,意思是Java平台把操作系统的底层给屏蔽起来,所以它可以在它自己的虚拟的平台里面构造出对自己有利的机制,而语言或者说

守护线程与非守护线程的区别

守护线程与非守护线程 最近在看多线程的Timer章节,发现运用到了守护线程,感觉Java的基础知识还是需要补充. Java分为两种线程:用户线程和守护线程 所谓守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分.因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程.反过来说,只要任何非守护线程还在运行,程序就不会终止. 守护线程和用户线程的没啥本质的区别:唯一的不同之处就在于虚拟机

5.UI线程和非UI线程的交互方式

这里说的交互方式应该指的是如何在非UI线程中修改UI线程中的组件.      一般来说有三种方式: 1.Activity.unOnUiThread(Runnable) 如果当前线程是UI Thread,立马执行action.run方法:否则将Runnable发送到UI Thread的event 队列中. 2. view.post(Runnable) 将action加入到UI thread 的message queue. 3.view.postDelayed(Runnable,long)同2一样,