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

Java Swing開發中的線程安全

2022-06-13   來源: Java高級技術 

  SwingAPI的設計目標是強大靈活和易用非凡地我們希望能讓程序員們方便地建立新的Swing組件不論是從頭開始還是通過擴展我們所提供的一些組件出於這個目的我們不要求Swing組件支持多線程訪問相反我們向組件發送請求並在單一線程中執行請求本文討論線程和Swing組件目的不僅是為了幫助你以線程安全的方式使用SwingAPI而且解釋了我們為什麼會選擇現在這樣的線程方案本文包括以下內容

  單線程規則Swing線程在同一時刻僅能被一個線程所訪問一般來說這個線程是事件派發線程規則的例外有些操作保證是線程安全的事件分發假如你需要從事件處理或繪制代碼以外的地方訪問UI那麼你可以使用SwingUtilities類的invokeLater要求在事件派發線程中執行某些代碼這個方法會立即返回不會等待代碼執行完畢invokeAndWait行為與invokeLater類似除了這個方法會等待代碼執行完畢一般地你可以用invokeLater來代替這個方法下面是一些使用這幾個API的例子請同時參閱《TheJavaTutorial》中的BINGOexample尤其是以下幾個類CardWindowControlPanePlayer和OverallStatusPane

  使用invokeLater方法你可以從任何線程調用invokeLater方法以請求事件派發線程運行特定代碼你必須把要運行的代碼放到一個Runnable對象的run方法中並將此Runnable對象設為invokeLater的參數invokeLater方法會立即返回不等待事件派發線程執行指定代碼這是一個使用invokeLater方法的例子

  
 RunnabledoWorkRunnable=newRunnable
  };

  SwingUtilitiesinvokeLater;使用invokeAndWait方法invokeAndWait方法和invokeLater方法很相似除了invokeAndWait方法會等事件派發線程執行了指定代碼才返回在可能的情況下你應該盡量用invokeLater來代替invokeAndWait假如你真的要使用invokeAndWait請確保調用invokeAndWait的線程不會在調用期間持有任何其他線程可能需要的鎖

  這是一個使用invokeAndWait的例子

  
 voidshowHelloThereDialogthrowsException
  };
  SwingUtilitiesinvokeAndWait;
  }

  類似地假設一個線程需要對GUI的狀態進行存取比如文本域的內容它的代碼可能類似這樣

  
 voidprintTextField
  throwsException
  };
  SwingUtilitiesinvokeAndWait;
  Systemoutprintln;}

  假如你能避免使用線程最好這樣做線程可能難於使用並使得程序的debug更困難一般來說對於嚴格意義下的GUI工作線程是不必要的比如對組件屬性的更新不管怎麼說有時候線程是必要的下列情況是使用線程的一些典型情況執行一項費時的任務而不必將事件派發線程鎖定例子包括執行大量計算的情況會導致大量類被裝載的情況和為網絡或磁盤I/O而阻塞的情況重復地執行一項操作通常在兩次操作間間隔一個預定的時間周期要等待來自客戶的消息你可以使用兩個類來幫助你實現線程SwingWorker創建一個後台線程來執行費時的操作Timer創建一個線程來執行或多次執行某些代碼在兩次執行間間隔用戶定義的延遲使用SwingWorker類SwingWorker類在SwingWorkerjava中實現這個類並不包含在Java的任何發行版中所以你必須單獨下載它SwingWorker類做了所有實現一個後台線程所需的骯髒工作雖然許多程序都不需要後台線程後台線程在執行費時的操作時仍然是很有用的它能提高程序的性能觀感

  SwingWorkersanexampleofusingSwingWorker要使用SwingWorker類你首先要實現它的一個子類在子類中你必須實現construct方法還包含你的長時間操作當你實例化SwingWorker的子類時SwingWorker創建一個線程但並不啟動它你要調用你的SwingWorker對象的start方法來啟動線程然後start方法會調用你的construct方法當你需要construct方法返回的對象時可以調用SwingWorker類的get方法這是一個使用SwingWorker類的例子

  
 //在main方法中
  finalSwingWorkerworker=newSwingWorker
  };
  workerstart;
  
  //在動作事件處理方法中
  JOptionPaneshowMessageDialog)

  當程序的main方法調用start方法SwingWorker啟動一個新的線程來實例化ExpensiveDialogComponentmain方法還構造了由一個窗口和一個按鈕組成的GUI當用戶點擊按鈕程序將阻塞假如必要阻塞到ExpensiveDialogComponent創建完成然後程序顯示一個包含ExpensiveDialogComponent的模式對話框你可以在MyApplicationjava找到整個程序使用Timer類Timer類通過一個ActionListener來執行或多次執行一項操作你創建定時器的時候可以指定操作執行的頻率並且你可以指定定時器的動作事件的監聽者啟動定時器後動作監聽者的actionPerformed方法會被調用來執行操作定時器動作監聽者定義的actionPerformed方法將在事件派發線程中調用這意味著你不必在其中使用invokeLater方法這是一個使用Timer類來實現動畫循環的例子

  
 publicclassAnimatorApplicationTimer
  extendsJFrameimplementsActionListener
  publicvoidstartAnimationelse
  }
  publicvoidstopAnimation
  publicvoidactionPerformed
  
  }

  在一個線程中執行所有的用戶界面代碼有這樣一些優點組件開發者不必對線程編程有深入的理解像ViewPoint和Trestle這類工具包中的所有組件都必須完全支持多線程訪問使得擴展非常困難尤其對不精通線程編程的開發者來說最近的一些工具包如SubArctic和IFC都采用和Swing類似的設計事件以可預知的次序派發invokeLater排隊的runnable對象從鼠標和鍵盤事件定時器事件繪制請求的同一個隊列派發在一些組件完全支持多線程訪問的工具包中組件的改變被變化無常的線程調度程序穿插到事件處理過程中這使得全面測試變得困難甚至不可能更低的代價嘗試小心鎖住臨界區的工具包要花費實足的時間和空間在鎖的治理上每當工具包中調用某個可能在客戶代碼中實現的方法時工具包都要保存它的狀態並釋放所有鎖以便客戶代碼能在必要時獲得鎖當控制權交回到工具包工具包又必須重新抓住它的鎖並恢復狀態所有應用程序都不得不負擔這一代價即使大多數應用程序並不需要對GUI的並發訪問這是的SubArcticJavaToolkit的對在工具包中支持多線程訪問的問題的描述我們的基本信條是當設計和建造多線程應用程序尤其是那些包括GUI組件的應用程序時必須保證極端小心線程的使用可能會很有欺騙性在許多情況下它們表現得能夠極好的簡化編成使得設計專注於單一任務的簡單自治實體成為可能在一些情況下它們的確簡化了設計和編碼然而在幾乎所有的情況下它們都使得調試測試和維護的困難大大增加甚至成為不可能無論大多數程序員所受的練習他們的經驗和實踐還是我們用來幫助自己的工具都不是能夠用來對付非決定論的例如全面測試在bug依靠於時間時是幾乎不可能的尤其對於Java來說一個程序要運行在許多不同類型的機器的操作系統平台上並且每個程序都必須在搶先和非搶先式調度下都能正常工作由於這些固有的困難我們力勸你三思是否絕對有使用線程的必要盡管如此有些情況下使用線程是必要的所以subArctic提供了一個線程安全的訪問機制

  本章討論了這一機制和怎樣在一個獨立線程中安全地操作交互樹他們所說的線程安全機制非常類似於SwingUtilities類提供的invokeLater和invokeAndWait方法


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