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线程
主线程是UI线程
监听器方法中是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();
                }
            }
        }
    });

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

uithread.PNG

避免无响应
那么,如何避免程序进入无响应状态呢? 其实很简单,不要在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类的s`yncExec(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秒的无响应状态中, 也相当于新开一个线程执行耗时操作的目的就没有达到了。

读者可以试着根据上面的例子,自己写一个时钟程序,每秒钟刷新显示当前时间,注意不要让程序陷入无响应状态。 可以参考示例代码

转自http://blog.csdn.net/dollyn/article/details/38582743

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值