敏捷是一條很長的路,摸索著前進著
現在我們要做一個簡單的界面。
包括一個進度條、一個輸入框、開始和停止按鈕。
需要實現的功能是:
當點擊開始按鈕,則更新進度條,并且在輸入框內把完成的百分比輸出(這里只做例子,沒有真正去做某個工作)。
運行代碼發現,
現象1:當點擊了開始按鈕,畫面就卡住了。按鈕不能點擊,進度條沒有被更新,輸入框上也沒有任何信息。
原因分析:Swing是線程不安全的,是單線程的設計,所以只能從事件派發線程訪問將要在屏幕上繪制的Swing組件。ActionListener的actionPerformed方法是在事件派發線程中調用執行的,而點擊了開始按鈕后,執行了go()方法,在go()里,雖然也去執行了更新組件的方法
progressBar.setValue(count);
text.setText(STR + String.valueOf(count) + "%");
但由于go()方法直到循環結束,它并沒有返回,所以更新組件的操作一直沒有被執行,這就造成了畫面卡住的現象。
現象2:過了一段時間(go方法里的循環結束了)后,畫面又可以操作,并且進度條被更新,輸入框也出現了我們想看到的信息。
原因分析:通過在現象1的分析,很容易聯想到,當go()方法返回了,則其他的線程(更新組件)可以被派發了,所以畫面上的組件被更新了。
為了讓畫面不會卡住,我們來修改代碼,將耗時的工作放在一個線程里去做。
代碼2:
我們執行了程序,結果和我們想要的一樣,畫面不會卡住了。
那這個程序是否沒有問題了呢?
我們自定義了一個線程GoThread,在這里我們完成了那些耗時的工作,可以看作是“工作線程”,
而對于組件的更新,我們也放在了“工作線程”里完成了。
在這里,在事件派發線程以外的線程里設置進度條,是一個危險的操作,運行是不正常的。(對于輸入框組件的更新是安全的。)
只有從事件派發線程才能更新組件,根據這個原則,我們來修改我們現有代碼。
解釋:SwingUtilities.invokeLater()方法使事件派發線程上的可運行對象排隊。當可運行對象排在事件派發隊列的隊首時,就調用其run方法。其效果是允許事件派發線程調用另一個線程中的任意一個代碼塊。
還有一個方法SwingUtilities.invokeAndWait()方法,它也可以使事件派發線程上的可運行對象排隊。
他們的不同之處在于:SwingUtilities.invokeLater()在把可運行的對象放入隊列后就返回,而SwingUtilities.invokeAndWait()一直等待知道已啟動了可運行的run方法才返回。如果一個操作在另外一個操作執行之前必須從一個組件獲得信息,則應使用SwingUtilities.invokeAndWait()方法。 ----2009年02月16日 本文為原創,歡迎轉載,轉載請注明出處BlogJava。
Powered by: BlogJava Copyright © 李 明