gembin

          OSGi, Eclipse Equinox, ECF, Virgo, Gemini, Apache Felix, Karaf, Aires, Camel, Eclipse RCP

          HBase, Hadoop, ZooKeeper, Cassandra

          Flex4, AS3, Swiz framework, GraniteDS, BlazeDS etc.

          There is nothing that software can't fix. Unfortunately, there is also nothing that software can't completely fuck up. That gap is called talent.

          About Me

           

          Eclipse RCP中多線程Job使用

          本文分析了Eclipse中多線程程序的實(shí)現(xiàn),討論了在Eclipse客戶端程序開發(fā)中應(yīng)用多線程的方法和要注意的問題,同時(shí)也討論了多線程程序的一些調(diào)試和問題解決的方法。

          Eclipse 作為一個(gè)開發(fā)平臺(tái),使用越來越廣泛,基于Eclipse Rich Client Platform開發(fā)的客戶端程序也越來越多。在當(dāng)今越來越復(fù)雜的應(yīng)用環(huán)境中,我們的客戶端程序不可避免的要同時(shí)進(jìn)行多任務(wù)的處理。一個(gè)優(yōu)異的客戶端程序 都會(huì)允許用戶同時(shí)啟動(dòng)多個(gè)任務(wù),從而大大提高用戶的工作效率以及用戶體驗(yàn)。本文中我們來談?wù)凟clipse中實(shí)現(xiàn)多任務(wù)的方式。

          在 我們基于Eclipse的Java程序中,我們有很多種方式提供多任務(wù)的實(shí)現(xiàn)。熟悉Java的朋友立即會(huì)想到Java的Thread類,這是Java中使 用最多的一個(gè)實(shí)現(xiàn)多任務(wù)的類。Eclipse平臺(tái)為多任務(wù)處理提供了自己的API,那就是Job以及UIJob。Eclipse中的Job是對(duì)Java Thread的一個(gè)封裝,為我們實(shí)現(xiàn)多任務(wù)提供了更方便的接口。以下是Job的基本用法:

          清單 1. Job用法示例

                          Job job = new Job(“Job Name”){
          protected IStatus run(IProgressMonitor monitor) {

          // 在這里添加你的任務(wù)代碼
          return Status.OK_STATUS;
          }
          };
          job.schedule(1133);//delaytime
          job.setUser(true);//if true show UI
          job.setPriority(priority)

          在Eclipse 中我們也會(huì)經(jīng)常用到Display.asynchExec() 和Display.synchExec()來啟動(dòng)任務(wù)的執(zhí)行。這兩個(gè)方法主要為了方便我們完成界面操作的任務(wù)。以下是 Display.asynchExec()的用法,Display.synchExec()和它類似。


          清單 2. Display.synchExec()用法示例
                 Display.getDefault().asyncExec(new Runnable() {
          public void run() {
          // 在這里添加你的任務(wù)代碼
          }
          });

          通常,在Eclipse中我們最好使用Eclipse提供的Job接口來實(shí)現(xiàn)多任務(wù),而不是使用Java的thread。為什么呢?主要有以下幾個(gè)原因:

          • Job是可重用的工作單元,一個(gè)Job我們可以很方便的讓它多次執(zhí)行。
          • Job提供了方便的接口,使得我們?cè)谔幚碇心軌蚝芊奖愕呐c外界交流,報(bào)告當(dāng)前的執(zhí)行進(jìn)度
          • Eclipse提供了相應(yīng)的機(jī)制使得程序員可以方便的介入Job的調(diào)度,例如我們可以方便的實(shí)現(xiàn)每次只有一個(gè)同一類型的Job在運(yùn)行
          • Eclipse缺省提供了Job管理的程序,可以查看當(dāng)前所有的Job和它們的進(jìn)度,也提供UI終止、暫停、繼續(xù)指定的Job
          • 使 用Job可以提高程序的性能,節(jié)省線程創(chuàng)建和銷毀的開銷。Eclipse中的Job封裝了線程池的實(shí)現(xiàn)。當(dāng)我們啟動(dòng)一個(gè)Job時(shí),Eclipse不會(huì)馬上 新建一個(gè)Thread,它會(huì)在它的線程池中尋找是否有空閑的線程,如果有空閑線程,就會(huì)直接用空閑線程運(yùn)行你的Job。一個(gè)Job終止時(shí),它所對(duì)應(yīng)的線程 也不會(huì)立即終止,它會(huì)被返回到線程池中以備重復(fù)利用。這樣,我們可以節(jié)省創(chuàng)建和銷毀線程的開銷

          下面我們從幾個(gè)方面來討論Eclipse中Job的實(shí)現(xiàn)和使用方面的問題。

          Eclipse中Job的實(shí)現(xiàn)

          Eclipse 的核心包中提供了一個(gè)JobManager類,它實(shí)現(xiàn)了IJobManager接口,Eclipse中Job的管理和調(diào)度都是由JobManager來實(shí) 現(xiàn)的。 JobManager維護(hù)有一個(gè)線程池,用來運(yùn)行Job。當(dāng)我們調(diào)用Job的schedule方法后,這個(gè)Job會(huì)被JobManager首先放到一個(gè) Job運(yùn)行的等待隊(duì)列中去。之后,JobManager會(huì)通知線程池有新的Job加入了運(yùn)行等待隊(duì)列。線程池會(huì)找出一個(gè)空閑的線程來運(yùn)行Job,如果沒有 空閑線程,線程池會(huì)創(chuàng)建一個(gè)新的線程來運(yùn)行Job。一旦Job運(yùn)行完畢,運(yùn)行Job的線程會(huì)返回到線程池中以備下次使用。 從上面Job運(yùn)行的過程我們可以看到,JobManager介入了一個(gè)Job運(yùn)行的全過程,它了解Job什么時(shí)候開始,什么時(shí)候結(jié)束,每一時(shí)候Job的運(yùn) 行狀態(tài)。JobManager將這些Job運(yùn)行的信息以接口的方式提供給用戶,同時(shí)它也提供了接口讓我們可以介入Job的調(diào)度等,從而我們擁有了更加強(qiáng)大 的控制Job的能力。

          為了我們更方便的了解Job所處的狀態(tài),JobManager設(shè)置Job的一個(gè)狀態(tài)標(biāo)志位,我們可以通過Job的getState方法獲得Job當(dāng)前的狀態(tài)值以了解其狀態(tài):

          • NONE:當(dāng)一個(gè)Job剛構(gòu)造的時(shí)候,Job就會(huì)處于這種狀態(tài)。當(dāng)一個(gè)Job執(zhí)行完畢(包括被取消)后,Job的狀態(tài)也會(huì)變回這種狀態(tài)。
          • WAITING:當(dāng)我們調(diào)用了Job的shedule方法,JobManager會(huì)將Job放入等待運(yùn)行的Job隊(duì)列,這時(shí)Job的狀態(tài)為WAITING.
          • RUNNING:當(dāng)一個(gè)Job開始執(zhí)行,Job的狀態(tài)會(huì)變?yōu)镽UNNING。
          • SLEEPING: 當(dāng)我們調(diào)用Job的sleep方法后,Job會(huì)變成這一狀態(tài)。當(dāng)我們調(diào)用schudule方法的時(shí)候帶上延時(shí)的參數(shù),Job的狀態(tài)也會(huì)轉(zhuǎn)入這一狀態(tài),在這 一段延時(shí)等待的時(shí)間中,Job都處于這一狀態(tài)。這是一種睡眠狀態(tài),Job在這種狀態(tài)中時(shí)不能馬上轉(zhuǎn)入運(yùn)行。我們可以調(diào)用Job的wakeup方法來將 Job喚醒。這樣,Job又會(huì)轉(zhuǎn)入WAITING狀態(tài)等待運(yùn)行。

          Eclipse中的UI線程

          另 外,在Eclipse的線程處理中,有一個(gè)UI線程的概念。Eclipse程序中的主線程是一個(gè)特殊的線程,程序啟動(dòng)后會(huì)先執(zhí)行這個(gè)線程,也就是我們的 main()函數(shù)所在的線程。作為桌面應(yīng)用程序,我們的主線程主要負(fù)責(zé)界面的響應(yīng)以及繪制界面元素,所以通常我們也叫它UI線程。

          以下代碼,編過SWT應(yīng)用程序的讀者會(huì)非常熟悉。它一般出現(xiàn)在main函數(shù)的結(jié)尾。下面來仔細(xì)分析一下它的詳細(xì)情況。

          //當(dāng)窗口未釋放時(shí)
          while (!shell.isDisposed()) {

          //如果display對(duì)象事件隊(duì)列中沒有了等待的事件,就讓該線程進(jìn)入等待狀態(tài)
          if (!display.readAndDispatch())
          display.sleep();

          }

          上面的程序?qū)嶋H上就是我們UI線程的處理 邏輯:當(dāng)程序啟動(dòng)后,UI線程會(huì)讀取事件等待隊(duì)列,看有沒有事件等待處理。如果有,它會(huì)進(jìn)行相應(yīng)處理,如果沒有它會(huì)進(jìn)入睡眠狀態(tài)。如果有新的事件到來,它 又會(huì)被喚醒,進(jìn)行處理。UI線程所需要處理的事件包括用戶的鼠標(biāo)和鍵盤操作事件,操作系統(tǒng)或程序中發(fā)出的繪制事件。一般來說,處理事件的過程也就是響應(yīng)用 戶操作的過程。

          一個(gè)好的桌面應(yīng)用程序需要對(duì)用戶的操作作出最快的響應(yīng),也就是說我們的UI線程必須盡快的處理 各種事件。從我們程序的角度來說,在UI線程中我們不能進(jìn)行大量的計(jì)算或者等待,否則用戶操作事件得不到及時(shí)的處理。通常,如果有大量的計(jì)算或者需要長(zhǎng)時(shí) 間等待(例如進(jìn)行網(wǎng)絡(luò)操作或者數(shù)據(jù)庫操作)時(shí),我們必須將這些長(zhǎng)時(shí)間處理的程序單獨(dú)開辟出一個(gè)線程來執(zhí)行。這樣雖然后臺(tái)運(yùn)行著程序,但也不會(huì)影響界面上的 操作。

          除主線程之外的所有線程都是非UI線程。 在Eclipse程序中,我們所有對(duì)界面元素的操作都必須放到UI線程中來執(zhí)行,否則會(huì)拋出Exception,所以我們要區(qū)分出UI線程和非UI線程,保證我們對(duì)UI的操作都在UI線程中執(zhí)行。

          如何判斷當(dāng)前線程是否UI線程: 你可以通過調(diào)用Display.getCurrent()來知道當(dāng)前線程是否是UI線程。如果Display.getCurrent()返回為空,表示當(dāng)前不是UI線程。

          Eclipse中使用線程的幾種典型情況

          • 控制Job的并發(fā)運(yùn)行

          對(duì) 于某些Job,為了避免并發(fā)性問題,我們希望同時(shí)只有一個(gè)這樣的Job在運(yùn)行,這時(shí)我們需要控制Job的并發(fā)運(yùn)行。在另一種情況下,我們也需要控制Job 的并發(fā)運(yùn)行:我們?cè)诔绦蛑袑?duì)于一個(gè)任務(wù),我們有可能會(huì)啟動(dòng)一個(gè)Job來執(zhí)行,對(duì)于少量的任務(wù)來說,這是可行的,但是如果我們預(yù)測(cè)可能會(huì)同時(shí)有大量的任務(wù), 如果每一個(gè)任務(wù)啟動(dòng)一個(gè)Job,我們同時(shí)啟動(dòng)的Job就會(huì)非常多。這些Job會(huì)侵占大量的資源,影響其他任務(wù)的執(zhí)行。 我們可以使用Job的rule來實(shí)現(xiàn)控制Job的并發(fā)執(zhí)行。簡(jiǎn)單的我們可以通過下面的代碼實(shí)現(xiàn)。我們先定義一個(gè)如下rule:

          	private ISchedulingRule Schedule_RULE = new ISchedulingRule() {
          public boolean contains(ISchedulingRule rule) {
          return this.equals(rule);
          }
          public boolean isConflicting(ISchedulingRule rule) {
          return this.equals(rule);
          }
          };

          對(duì)于需要避免同時(shí)運(yùn)行的Job,我們可以將它們的rule設(shè)成上面定義的rule。如:

          	myjob1.setRule(Schedule_RULE);
          myjob2.setRule(Schedule_RULE);

          這樣對(duì)于myjob1和myjob2這兩個(gè)Job,它們不會(huì)再同時(shí)執(zhí)行。Myjob2會(huì)等待myjob1執(zhí)行完再執(zhí)行。這是由Eclipse的JobManager來提供實(shí)現(xiàn)的。JobManager可以保證所有啟動(dòng)的Job中,任意兩個(gè)Job的rule是沒有沖突的。 我們?cè)谏厦娑x的rule是最簡(jiǎn)單的。我們可以重寫isConflicting函數(shù)來實(shí)現(xiàn)一些更加復(fù)雜的控制,比如控制同時(shí)同類型的Job最多只有指定的個(gè)數(shù)在運(yùn)行。 但是我們要注意,isConflicting方法不能過于復(fù)雜。一旦一個(gè)Job的rule與其他Job的rule有沖突,isConflicting方法會(huì)調(diào)用很多次。如果其中的計(jì)算過于復(fù)雜,會(huì)影響整體的性能。

          • 根據(jù)需要執(zhí)行Job

          由 于我們有的Job有可能不是立即執(zhí)行的,在有些情況下,等到該Job準(zhǔn)備執(zhí)行的時(shí)候,該Job所要執(zhí)行的任務(wù)已經(jīng)沒有意義了。這時(shí),我們可以使用Job的 shouldSchedule()和shouldRun()來避免Job的運(yùn)行。在我們定義一個(gè)Job時(shí),我們可以重載shouldSchedule和 shouldRun方法。在這些方法中,我們可以檢查Job運(yùn)行的一些先決條件,如果這些條件不滿足,我們就可以返回false。JobManager在 安排Job運(yùn)行時(shí),它會(huì)先調(diào)用該Job的shouldSchedule方法,如果返回為false,JobManager就不會(huì)再安排這個(gè)Job運(yùn)行了。 同樣,JobManager在真正啟動(dòng)一個(gè)線程運(yùn)行一個(gè)Job前,它會(huì)調(diào)用該Job的shouldRun方法,如果返回false,它不再運(yùn)行這個(gè) Job。在下面的例子中,我們希望啟動(dòng)一個(gè)Job在十秒鐘之后更新文本框中的內(nèi)容。為了保證我們的Job運(yùn)行時(shí)是有意義的,我們需要確保我們要更新的文本 框沒有被銷毀,我們重載了shouldSchedule和shouldRun方法。

          Text text = new Text(parent,SWT.NONE);
          UIJob refreshJob = new UIJob(“更新界面”){
          public IStatus runInUIThread(IProgressMonitor monitor) {

          text.setText(“新文本”);
          return Status.OK_STATUS;
          }
          public boolean shouldSchedule(){
          return !text.isDisposed();
          }
          public boolean shouldRun(){
          return !text.isDisposed();
          }
          };
          refreshJob.schedule(10000);

          • 在UI線程中涉及長(zhǎng)時(shí)間處理的任務(wù)

          我 們經(jīng)常碰到這樣一種情況:用戶操作菜單或者按鈕會(huì)觸發(fā)查詢大量數(shù)據(jù),數(shù)據(jù)查詢完后更新表格等界面元素。用戶點(diǎn)擊菜單或者按鈕所觸發(fā)的處理程序一般處于UI 線程,為了避免阻塞UI,我們必須把數(shù)據(jù)查詢等費(fèi)時(shí)的工作放到單獨(dú)的Job中執(zhí)行,一旦數(shù)據(jù)查詢完畢,我們又必須更新界面,這時(shí)我們又需要使用UI線程進(jìn) 行處理。下面是處理這種情況的示例代碼:

          	button.addSelectionListener(new SelectionListener(){
          public void widgetSelected(SelectionEvent e){
          perform();
          }

          public void widgetDefaultSelected(SelectionEvent e){
          perform();
          }

          private void perform(){
          Job job = new Job(“獲取數(shù)據(jù)”){
          protected IStatus run(IProgressMonitor monitor){
          // 在此添加獲取數(shù)據(jù)的代碼
          Display.getDefault().asyncExec(new Runnable(){
          public void run(){
          // 在此添加更新界面的代碼
          }
          });
          }
          };
          job.schedule();
          }

          });

          • 延時(shí)執(zhí)行Job,避免無用的Job運(yùn)行

          我們經(jīng)常需要根據(jù)選中的對(duì)象刷新我們部分的界面元素。如果我們連續(xù)很快的改變選擇,而每次刷新界面涉及到的區(qū)域比較大時(shí),界面會(huì)出現(xiàn)閃爍。從用戶的角度來說,我們很快的改變選擇,希望看到的只是最后選中的結(jié)果,中間的界面刷新都是不必要的。

          在Jface 中,StructuredViewer提供了addPostSelectionChangedListener方法。如果我們使用這個(gè)方法監(jiān)聽 selectionChanged事件,當(dāng)用戶一直按著方向鍵改變選中時(shí),我們只會(huì)收到一個(gè)selectionChanged事件。這樣我們可以避免過度 的刷新界面。

          實(shí)際上,Jface中就是通過延時(shí)執(zhí)行Job來實(shí)現(xiàn)這一功能的。我們也可以自己實(shí)現(xiàn)類似功能:

          	private final static Object UPDATE_UI_JOBFAMILY = new Object();
          tableviewer. addSelectionChangedListener (new ISelectionChangedListener (){

          public void selectionChanged(SelectionChangedEvent event){
          Job.getJobManager().cancel(UPDATE_UI_JOBFAMILY);
          new UIJob("更新界面") {
          protected IStatus runInUIThread (IProgressMonitor monitor) {
          //更新界面
          return Status.OK_STATUS;
          }

          public boolean belongsTo(Object family){
          return family== UPDATE_UI_JOBFAMILY;
          }

          }.schedule(500);
          }
          });

          首 先,我們需要將界面更新的代碼放到一個(gè)UIJob中,同時(shí)我們將Job延時(shí)500毫秒執(zhí)行(我們可以根據(jù)需要改變延時(shí)的時(shí)間)。如果下一個(gè) selectionChanged事件很快到來,我們的調(diào)用Job.getJobManager().cancel (UPDATE_UI_JOBFAMILY)將以前未運(yùn)行的Job取消,這樣只有最后一個(gè)Job會(huì)真正運(yùn)行。

          • 在UI線程中等待非UI線程的結(jié)束

          有 時(shí),我們?cè)赨I線程中需要等待一個(gè)非UI線程執(zhí)行完,我們才能繼續(xù)執(zhí)行。例如,我們?cè)赨I線程中要顯示某些數(shù)據(jù),但是這些數(shù)據(jù)又需要從數(shù)據(jù)庫或者遠(yuǎn)程網(wǎng)絡(luò) 獲取。于是,我們會(huì)啟動(dòng)一個(gè)非UI的線程去獲取數(shù)據(jù)。而我們的UI線程必須要等待這個(gè)非UI線程執(zhí)行完成,我們才能繼續(xù)執(zhí)行。當(dāng)然,一種簡(jiǎn)單的實(shí)現(xiàn)方法是 使用join。我們可以在UI線程中調(diào)用非UI線程的join方法,這樣我們就可以等待它執(zhí)行完了,我們?cè)倮^續(xù)。但是,這會(huì)有一個(gè)問題。當(dāng)我們的UI線程 等待時(shí),意味著我們的程序不會(huì)再響應(yīng)界面操作,也不會(huì)刷新。這樣,用戶會(huì)覺得我們的程序象死了一樣沒有反應(yīng)。這時(shí),我們可以使用ModalContext 類。你可以將你要執(zhí)行的獲取數(shù)據(jù)的任務(wù)用ModalContext的run方法來運(yùn)行(如下)。ModalContext會(huì)將你的任務(wù)放到一個(gè)獨(dú)立的非 UI線程中執(zhí)行,并且等待它執(zhí)行完再繼續(xù)執(zhí)行。與join方法不同的是,ModalContext在等待時(shí)不會(huì)停止UI事件的處理。這樣我們的程序就不會(huì) 沒有響應(yīng)了。

          try {
          ModalContext.run(new IRunnableWithProgress(){
          public void run(IProgressMonitor monitor)
          throws InvocationTargetException, InterruptedException {
          /*需要在非UI線程中執(zhí)行的代碼*/
          ModalContext.checkCanceled(monitor);
          }
          }, true, new NullProgressMonitor(), Display.getCurrent());
          } catch (InvocationTargetException e) {

          } catch (InterruptedException e) {
          }

          • 針對(duì)相關(guān)聯(lián)的Job統(tǒng)一進(jìn)行處理

          有 時(shí),我們需要對(duì)相關(guān)聯(lián)的Job一起處理。例如需要同時(shí)取消這些Job,或者等待所有這些Job結(jié)束。這時(shí)我們可以使用Job Family。對(duì)于相關(guān)聯(lián)的Job,我們可以將它們?cè)O(shè)置成同一個(gè)Job Family。我們需要重載Job的belongsTo方法以設(shè)置一個(gè)Job的Job Family。

          Private Object MY_JOB_FAMILY = new Object();
          Job job = new Job(“Job Name”){
          protected IStatus run(IProgressMonitor monitor) {
          // 在這里添加你的任務(wù)代碼
          return Status.OK_STATUS;
          }

          public boolean belongsTo(Object family){
          return MY_JOB_FAMILY.equals(family);
          }
          };

          我們可以使用JobManager的一系列方法針對(duì)Job Family進(jìn)行操作:

          Job.getJobManager().cancel(MY_JOB_FAMILY); //取消所有屬于MY_JOB_FAMILY的所有Job
          Job.getJobManager().join(MY_JOB_FAMILY); //等待屬于MY_JOB_FAMILY的所有Job結(jié)束
          Job.getJobManager().sleep(MY_JOB_FAMILY); //將所有屬于MY_JOB_FAMILY的Job轉(zhuǎn)入睡眠狀態(tài)
          Job.getJobManager().wakeup(MY_JOB_FAMILY); //將所有屬于MY_JOB_FAMILY的Job喚醒

          線程死鎖的調(diào)試和解決技巧

          一 旦我們使用了線程,我們的程序中就有可能有死鎖的發(fā)生。一旦發(fā)生死鎖,我們發(fā)生死鎖的線程會(huì)沒有響應(yīng),導(dǎo)致我們程序性能下降。如果我們的UI線程發(fā)生了死 鎖,我們的程序會(huì)沒有響應(yīng),必須要重啟程序。所以在我們多線程程序開發(fā)中,發(fā)現(xiàn)死鎖的情況,解決死鎖問題對(duì)提高我們程序的穩(wěn)定性和性能極為重要。

          如果我們發(fā)現(xiàn)程序運(yùn)行異常(例如程序沒有響應(yīng)),我們首先要確定是否發(fā)生了死鎖。通過下面這些步驟,我們可以確定是否死鎖以及死鎖的線程:

          • 在Eclipse中以Debug模式運(yùn)行程序
          • 執(zhí)行響應(yīng)的測(cè)試用例重現(xiàn)問題
          • 在Eclipse的Debug View中選中主線程(Thread[main]),選擇菜單Run->Suspend。這時(shí)Eclipse會(huì)展開主線程的函數(shù)調(diào)用棧,我們就可以看到當(dāng)前主線程正在執(zhí)行的操作。
          • 通常,Eclipse在等待用戶的操作,它的函數(shù)調(diào)用棧會(huì)和以下類似:


            圖片示例
            圖片示例

          • 如果主線程發(fā)生死鎖,函數(shù)調(diào)用棧的最上層一般會(huì)是你自己的函數(shù)調(diào)用,你可以查看一下你當(dāng)前的函數(shù)調(diào)用以確定主線程在等待什么
          • 使用同樣的方法查看其他線程,特別是那些等待UI線程的線程

          我們需要找出當(dāng)前線程相互的等待關(guān)系,以便找出死鎖的原因。我們找出死鎖的線程后就可以針對(duì)不同情況進(jìn)行處理:

          • 減小鎖的粒度,增加并發(fā)性
          • 調(diào)整資源請(qǐng)求的次序
          • 將需要等待資源的任務(wù)放到獨(dú)立的線程中執(zhí)行

          Job使用中要注意的問題

          • 不 要在Job中使用Thread.sleep方法。如果你想要讓Job進(jìn)入睡眠狀態(tài),最好用Job的sleep方法。雖然,使用Thread.sleep和 Job的sleep方法達(dá)到的效果差不多,但是它們實(shí)現(xiàn)的方式完全不同,對(duì)系統(tǒng)的影響也不一樣。我們知道Eclipse中Job是由Eclipse的 JobManager來管理的。如果我們調(diào)用Job的sleep方法,JobManager會(huì)將Job轉(zhuǎn)入睡眠狀態(tài),與其對(duì)應(yīng)的線程也會(huì)重新放入線程池等 待運(yùn)行其他Job。而如果我們?cè)贘ob中直接調(diào)用Thread.sleep方法,它會(huì)直接使運(yùn)行Job的線程進(jìn)入睡眠狀態(tài),其他Job就不可能重用這個(gè)線 程了。同時(shí),雖然運(yùn)行該Job的線程進(jìn)入了睡眠狀態(tài),Job的狀態(tài)還是Running(運(yùn)行狀態(tài)),我們也不能用Job的wakeup方法喚醒該Job了
          • Job 的取消。一般我們會(huì)直觀的認(rèn)為,一旦調(diào)用Job的cancel方法,Job就會(huì)停止運(yùn)行。實(shí)際上,這并不一定正確,當(dāng)Job處于不同的狀態(tài)時(shí),我們調(diào)用 Job的cancel方法所起的效果是不同的。當(dāng)Job在WAITING狀態(tài)和SLEEPING狀態(tài)時(shí),一旦我們調(diào)用cancel方法, JobManager會(huì)將Job直接從等待運(yùn)行的隊(duì)列中刪除,Job不會(huì)再運(yùn)行了,這時(shí)cancel方法會(huì)返回true。但是如果Job正在運(yùn)行, cancel方法調(diào)用并不會(huì)立即終止Job的運(yùn)行,它只會(huì)設(shè)定一個(gè)標(biāo)志,指明這個(gè)Job已經(jīng)被取消了。我們可以使用Job的run方法傳入的參數(shù) IProgressMonitor monitor,這個(gè)參數(shù)的isCanceled方法會(huì)返回Job是否被取消的狀態(tài)。如果需要,我們必須在我們的代碼的適當(dāng)位置檢查Job是否被取消的標(biāo) 志,作出適當(dāng)?shù)捻憫?yīng)。另外,由于調(diào)用Job的cancel方法不一定立即終止Job,如果我們需要等待被取消的Job運(yùn)行完再執(zhí)行,我們可以用如下代碼:
             if (!job.cancel())
            job.join();

          • Join方法的使用。由于join方法會(huì)導(dǎo)致一個(gè)線程等待另一個(gè)線程,一旦等待線程中擁有一個(gè)被等待線程所需要的鎖,就會(huì)產(chǎn)生死鎖。當(dāng)我們的線程中需要用到同步時(shí),這種死鎖的情況非常容易出現(xiàn),所以我們使用join時(shí)必須非常小心,盡量以其他方法替代。
          • 避 免過時(shí)的Job造成的錯(cuò)誤。由于我們啟動(dòng)的線程并不一定是馬上執(zhí)行的,當(dāng)我們的Job開始運(yùn)行時(shí),情況可能發(fā)生了變化。我們?cè)贘ob的處理代碼中要考慮到 這些情況。一種典型的情況是,我們?cè)趩?dòng)一個(gè)對(duì)話框或者初始化一個(gè)ViewPart時(shí),我們會(huì)啟動(dòng)一些 Job去完成一些數(shù)據(jù)讀取的工作,一旦數(shù)據(jù)讀取結(jié)束,我們會(huì)啟動(dòng)新的UI Job更新相應(yīng)的UI。有時(shí),用戶在打開對(duì)話框或者View后,馬上關(guān)閉了該對(duì)話框或者View。這時(shí)我們啟動(dòng)的線程并沒有被中斷,一旦在Job中再去更 新UI,就會(huì)出錯(cuò)。在我們的代碼中必須作相應(yīng)的處理。所以,我們?cè)诰€程中更新界面元素之前,我們必須先檢查相應(yīng)的控件是否已經(jīng)被dispose了

          結(jié)束語

          在我們進(jìn)行基于Eclipse的客戶端開發(fā)時(shí),使用多線程可以大大的提供我們的程序并發(fā)處理能力,同時(shí)對(duì)于提高用戶體驗(yàn)也有很好的幫助。但是,多線程程序也有其不利的一面,我們也不要濫用線程:

          • 首先,多線程程序會(huì)大大的提高我們程序的復(fù)雜度,使得我們的開發(fā)和調(diào)試更加困難
          • 其次,過多的線程容易引發(fā)死鎖、數(shù)據(jù)同步等并發(fā)問題的發(fā)生
          • 另外,由于線程創(chuàng)建和銷毀需要開銷,程序的整體性能可能因?yàn)檫^多線程的使用而下降

          所以,我們?cè)谑褂镁€程時(shí)一定要謹(jǐn)慎。本文對(duì)Eclipse線程的討論,希望能對(duì)大家使用線程有所幫助。由于實(shí)際情況較為復(fù)雜,文中所提到的方法僅供參考,讀者對(duì)于不同的實(shí)際問題需要進(jìn)行具體分析,從而找出最佳的解決方案。

          posted on 2008-03-21 17:40 gembin 閱讀(6582) 評(píng)論(0)  編輯  收藏 所屬分類: Eclipse RCPSWT

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(6)

          隨筆分類(440)

          隨筆檔案(378)

          文章檔案(6)

          新聞檔案(1)

          相冊(cè)

          收藏夾(9)

          Adobe

          Android

          AS3

          Blog-Links

          Build

          Design Pattern

          Eclipse

          Favorite Links

          Flickr

          Game Dev

          HBase

          Identity Management

          IT resources

          JEE

          Language

          OpenID

          OSGi

          SOA

          Version Control

          最新隨筆

          搜索

          積分與排名

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          free counters
          主站蜘蛛池模板: 临安市| 漾濞| 留坝县| 左权县| 新干县| 安新县| 南部县| 桦川县| 台山市| 高雄县| 虎林市| 绵竹市| 右玉县| 濮阳市| 玛多县| 邢台市| 济阳县| 奎屯市| 庆阳市| 柏乡县| 林西县| 泽州县| 晋州市| 改则县| 新郑市| 襄垣县| 格尔木市| 江油市| 丰县| 泸西县| 林州市| 滦南县| 宿州市| 深水埗区| 普格县| 龙泉市| 周至县| 交城县| 长春市| 孝感市| 炉霍县|