线程安全与并发安全探究(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dongping1023/article/details/47317547

并发安全问题历来是一个极其重要的技术要点,特别是在高并发大流量数据处理中。下面就线程非安全举例,以便加深对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){…}

猜你喜欢

转载自blog.csdn.net/dongping1023/article/details/47317547
今日推荐