線程安全與併發安全探究(二)

併發安全問題歷來是一個極其重要的技術要點,特別是在高併發大流量數據處理中。下面就線程非安全舉例,以便加深對java中如何實現線程安全的理解。

public class ThreadUnsafe {
       static int k=0;
       static int []arr;
       public static voidmain(String[] args) {
              final  UnsafeSequence us = newUnsafeSequence();
              int len=100;
              //final int []arr = new int[len];
              arr = new int[len];
              CountDownLatch doneSignal = newCountDownLatch(len);
              for (int i = 0;i < len; i++) {
                     newThread(new WorkRunnable(us,doneSignal))
                     .start();
             
       }
              try {
                     doneSignal.await();
              } catch(InterruptedException e) {
                     e.printStackTrace();
              }
              System.out.println("thread has finished all");
              for(intm=0;m<arr.length;m++){
                     for(intn=m+1;n<arr.length;n++){
                            if(arr[m] == arr[n])
                            {
                                   System.out.println("exist same number !,the number is "+arr[m]+",index="+m+",index_other="+n);
                                   break;
                            }
                     }
              }
              Arrays.sort(arr);
              for(int item:arr){
                     System.out.print(item+" ");
              }
             
              }
       static classWorkRunnable implements Runnable{
              privateUnsafeSequence us;
              privateCountDownLatch cdl;
              publicWorkRunnable(UnsafeSequence us,CountDownLatch cdl){
                     this.us = us;
                     this.cdl = cdl;
              }
              @Override
              public void run(){
                     try {
                            Thread.sleep(5);
                     } catch(InterruptedException e) {
                            e.printStackTrace();
                     }
//對k進行加鎖 是爲了避免對k的讀寫併發錯誤,因爲我們此刻只關注UnSafeSequence類的併發安全性問題
                     synchronized(Integer.valueOf(k)){
                                   System.out.println(arr[k++]=us.getNext());
                     }
                     cdl.countDown();
              }
       }
       public static classUnsafeSequence{
              private int value;
              public intgetNext_other(){
                     int ret = value;
                            for(inti=0;i<100;i++) {
                            int xx= i/21;
                     }
                     value++;
                     return ret;
              }
              public intgetNext(){
                     return value++;
              }
      
       }
}
 


分析:

1)當使用getNext()時,其中的可能的一次結果如下:

0

2

4

7

6

8

5

3

14

29

30

31

32

37

1

36

35

34

33

28

27

26

25

24

40

23

22

46

21

19

20

51

18

17

52

16

15

13

12

11

10

9

71

70

69

68

67

87

88

66

65

64

63

62

61

60

59

58

57

56

55

54

53

50

49

48

47

45

44

43

42

41

39

38

98

97

96

95

94

93

92

91

90

89

86

85

84

83

82

81

81

80

79

78

77

76

75

74

73

72

thread has finished all

exist same number !,the number is81,index=81,index_other=82

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1718 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 4344 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 6970 71 72 73 74 75 76 77 78 79 80 81 81 82 83 8485 86 87 88 89 90 91 92 93 94 95 96 97 98

 

UnsafeSequence類在單線程環境中,可以正常工作,但在多線程環境中則不能。其問題在於:如果執行時機不對,那麼這兩個線程調用getNext()時可能會得到相同的值。雖然遞增運算value++看上去是單個操作,但事實上它包含三個獨立的操作:讀取value值;將value加1;並將計算結果寫入value。由於運行時可能將多個線程之間的操作交替執行,因此這兩個線程可能同時執行讀操作,從而使它們得到相同的值,並都將這個值加1(如下A\B執行流所示)。結果就是在不同線程的調用中返回的相同的值(如上述結果一樣)。

A: value->81                  81+1=82         value=82;

B:             value->81                  81+1=82           value=82

 

2)當使用getNext_other時,可能出現的一種結果是:

0

1

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

28

31

30

32

29

33

34

36

41

42

49

50

51

57

58

59

60

61

62

63

27

99

98

97

96

95

94

93

92

91

90

89

88

87

85

85

84

83

82

81

80

79

78

77

76

75

74

73

72

71

70

69

68

67

66

64

64

56

55

54

53

52

48

47

46

45

44

43

40

39

38

37

35

thread has finished all

exist same number !,the number is64,index=64,index_other=65

exist same number !,the number is85,index=85,index_other=86

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1718 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 4344 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 64 66 67 68 6970 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 85 87 88 89 90 91 92 93 94 9596 97 98 99

很明顯,此處的問題和(1)中所述類似。

 

需要解決上述兩個問題,可以採用對共享對象資源進行同步控制,即使用synchronized關鍵字加鎖。如將public int getNext() 改爲public synchronized int getValue()。或者在該方法裏面使用synchronized(this)或者在線程體中使用synchronized(us){…}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章