現(xiàn)在我們要做一個(gè)簡單的界面。
包括一個(gè)進(jìn)度條、一個(gè)輸入框、開始和停止按鈕。
需要實(shí)現(xiàn)的功能是:
當(dāng)點(diǎn)擊開始按鈕,則更新進(jìn)度條,并且在輸入框內(nèi)把完成的百分比輸出(這里只做例子,沒有真正去做某個(gè)工作)。




























































運(yùn)行代碼發(fā)現(xiàn),
現(xiàn)象1:當(dāng)點(diǎn)擊了開始按鈕,畫面就卡住了。按鈕不能點(diǎn)擊,進(jìn)度條沒有被更新,輸入框上也沒有任何信息。
原因分析:Swing是線程不安全的,是單線程的設(shè)計(jì),所以只能從事件派發(fā)線程訪問將要在屏幕上繪制的Swing組件。ActionListener的actionPerformed方法是在事件派發(fā)線程中調(diào)用執(zhí)行的,而點(diǎn)擊了開始按鈕后,執(zhí)行了go()方法,在go()里,雖然也去執(zhí)行了更新組件的方法
progressBar.setValue(count);
text.setText(STR + String.valueOf(count) + "%");
但由于go()方法直到循環(huán)結(jié)束,它并沒有返回,所以更新組件的操作一直沒有被執(zhí)行,這就造成了畫面卡住的現(xiàn)象。
現(xiàn)象2:過了一段時(shí)間(go方法里的循環(huán)結(jié)束了)后,畫面又可以操作,并且進(jìn)度條被更新,輸入框也出現(xiàn)了我們想看到的信息。
原因分析:通過在現(xiàn)象1的分析,很容易聯(lián)想到,當(dāng)go()方法返回了,則其他的線程(更新組件)可以被派發(fā)了,所以畫面上的組件被更新了。
為了讓畫面不會卡住,我們來修改代碼,將耗時(shí)的工作放在一個(gè)線程里去做。
代碼2:

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56


57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

我們執(zhí)行了程序,結(jié)果和我們想要的一樣,畫面不會卡住了。
那這個(gè)程序是否沒有問題了呢?
我們自定義了一個(gè)線程GoThread,在這里我們完成了那些耗時(shí)的工作,可以看作是“工作線程”,
而對于組件的更新,我們也放在了“工作線程”里完成了。
在這里,在事件派發(fā)線程以外的線程里設(shè)置進(jìn)度條,是一個(gè)危險(xiǎn)的操作,運(yùn)行是不正常的。(對于輸入框組件的更新是安全的。)
只有從事件派發(fā)線程才能更新組件,根據(jù)這個(gè)原則,我們來修改我們現(xiàn)有代碼。

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64


65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

解釋:SwingUtilities.invokeLater()方法使事件派發(fā)線程上的可運(yùn)行對象排隊(duì)。當(dāng)可運(yùn)行對象排在事件派發(fā)隊(duì)列的隊(duì)首時(shí),就調(diào)用其run方法。其效果是允許事件派發(fā)線程調(diào)用另一個(gè)線程中的任意一個(gè)代碼塊。
還有一個(gè)方法SwingUtilities.invokeAndWait()方法,它也可以使事件派發(fā)線程上的可運(yùn)行對象排隊(duì)。
他們的不同之處在于:SwingUtilities.invokeLater()在把可運(yùn)行的對象放入隊(duì)列后就返回,而SwingUtilities.invokeAndWait()一直等待知道已啟動(dòng)了可運(yùn)行的run方法才返回。如果一個(gè)操作在另外一個(gè)操作執(zhí)行之前必須從一個(gè)組件獲得信息,則應(yīng)使用SwingUtilities.invokeAndWait()方法。
----2009年02月16日
本文為原創(chuàng),歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處BlogJava。