SWT的UI线程和非UI线程
為什么80%的碼農都做不了架構師?>>> ??
要理解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線程中執行長時間的操作,如果必需要執行費時操作,就在另外的線程中執行:
ok.addSelectionListener(new SelectionAdapter() {@Overridepublic 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() {@Overridepublic 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 accessat 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() {@Overridepublic void widgetSelected(SelectionEvent e) {// 此處代碼直接在監聽器方法中,是UI線程new Thread() {public void run() {// 此處為另外一個單獨線程,非UI線程 while(true) {// 非UI線程訪問UIdisplay.syncExec(new Runnable() {@Overridepublic 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秒的無響應狀態中, 也相當于新開一個線程執行耗時操作的目的就沒有達到了。
讀者可以試著根據上面的例子,自己寫一個時鐘程序,每秒鐘刷新顯示當前時間,注意不要讓程序陷入無響應狀態。 可以參考示例代碼。
轉載于:https://my.oschina.net/dollyn/blog/360620
總結
以上是生活随笔為你收集整理的SWT的UI线程和非UI线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS系列:CSS中盒子模型
- 下一篇: PHP zendframework ph