线程安全

        线程安全的定义:当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程如何交替执行,并且在主调用代码中不需要额外的同步和协调,这个类都能表现出正确的行为,那么这个类就是线程安全的。

       线程安全性可能是非常复杂的,在没有充足同步的情况下,多个线程中的操作执行顺序是不可预测的,甚至会产生非常奇怪的结果。

        如果当多个线程访问一个可变的共享变量时没有使用合适的同步,那么线程就会出现错误。有三种方式可以修复这个问题:

  1. 不在线程间共享变量
  2. 将可变的变量改为不可变的变量
  3. 在访问时使用同步。

有这样一个需求,需要按照序列顺序产生一个序列(不能有重复数据)

 

自增,看似是一个原子操作实际上一个自增包含三个操作步骤:获取变量值, 将变量值加1,将计算结果写入变量。

 

 

/**
 *
 * @author zhangwei_david
 * @version $Id: UnsafeSequence.java, v 0.1 2014年10月24日 下午9:38:54 zhangwei_david Exp $
 */
public class UnsafeSequence {

    
        public static UnsafeSequence unsafeSequence = new UnsafeSequence();
    

    public static UnsafeSequence getInstance() {
        return unsafeSequence;
    }

    private int value;

    public int getNext() {
        return value++;
    }
}

  这个类在单线程下是没有任何问题的,可以顺序的生成一个序列。

 

 

/**
 *
 * @author zhangwei_david
 * @version $Id: OneThread.java, v 0.1 2014年10月25日 下午9:50:32 zhangwei_david Exp $
 */
public class OneThread {

    /**
     *
     * @param args
     */
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            System.out.println(UnsafeSequence.getInstance().getNext());
        }
    }

}

 

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
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

 那么在多项线程下有是什么样的情况呢?

 

 

import java.util.concurrent.TimeUnit;

/**
 *
 * @author zhangwei_david
 * @version $Id: Test.java, v 0.1 2014年10月24日 下午9:40:41 zhangwei_david Exp $
 */
public class Test {

    /**
     *
     * @param args
     */
    public static void main(String[] args) {

        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {

                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                    }
                    System.out.println(UnsafeSequence.getInstance().getNext());
                }
            }).start();
        }
    }
}
0
15
18
17
21
24
28
16
14
2
0
1
13
12
5
10
31
33
37
11
6
8
9
7
41
43
40
39
38
36
34
35
45
49
58
32
30
29
27
26
25
23
22
4
20
3
19
88
87
86
85
83
84
80
82
81
79
78
77
76
89
90
74
75
73
72
71
70
94
95
69
67
68
66
65
64
63
97
62
61
59
60
57
56
55
54
53
52
51
50
48
47
46
44
42
98
96
93
92
91

        我们可以发现在多线程下,打印的结果是乱序的。 是不是以此就可以断定这个类不是线程安全的内。当然不能,但因的结果是由线程调度和执行的时间决定的。 如果是线程安全的,最终的结果应该自增到99,可是最终没有自增到99,仔细查询结果可以发现有打印了连个零,也就是说有两个线程访问了同一个值,以此可以断定这个类不是线程安全的。

      这是由于多线程要共享相同的内存地址空间,并且是并发运行,因此它们可能会访问或修改其他线程正在使用的变量。如果需要是共享的变量的行为可以预测就需要使用同步。如果没有使用同步,那么无论是编译器、硬件还是在运行时都可以对操作进行优化重新排序,这有助有提升性能但也为开发人员带来了负担。

 那么如何将这个类改为线程安全的呢? 我们只需将获取下一个值的方法改为同步方法既可以解决这个问题。

 

/**
 *
 * @author zhangwei_david
 * @version $Id: Sequence.java, v 0.1 2014年10月24日 下午9:51:20 zhangwei_david Exp $
 */
public class SafeSequence {
    private static class InstanceHolder {
        public static SafeSequence instance = new SafeSequence();
    }

    public SafeSequence getInstance() {
        return InstanceHolder.instance;
    }

    private volatile int value = 0;

    public synchronized int getNext() {
        return value++;
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值