熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java開源技術 >> 正文

關於Eclipse中UI程序的線程的討論

2013-11-23 20:10:47  來源: Java開源技術 
雖然各個操作系統之間的線程機制是不一樣的但是大致是相同的當用戶使用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);就可以了


From:http://tw.wingwit.com/Article/program/Java/ky/201311/28076.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.