正文
BadCount1
我来举一个例子,例如下面的代码
这里我定义了一个 Count 的接口,用来计数,方法有增加计数 add,减少计数 remove 以及获取当前计数 getCount
我根据这个接口实现了一个糟糕的计数类 BadCount1,该类可以用来记录某个房间到底进入了多少人,当有人进入则调用 add(),出去调用 remove()
我写了个测试类 CountThread 来测试这个计数类有多糟糕,它是一个线程类,线程中的方法是进入房间然后出去100次,测试方法 check 表示启动十个测试类,每个进出100次,当所有线程执行结束,打印房间中的人数,很明显理论上应该是0,然而结果有可能不是甚至是负数
1 | public interface Count { |
观察上面的代码,我们主要看以下线程中的方法
1 | for (int i = 0; i < 100; i++) { |
这个方法中调用的 add 和 remove 针对 count 变量做了 ++ 和 - - 操作,有可能发生如下操作
WorseCount1
上面 BadCount1 可能需要多执行几次才会看到效果,为了更快的出错,我写了个更糟糕的计数类 WorseCount1
1 | public class WorseCount1 implements Count { |
那么怎么将这个糟糕的计数类变成安全可用的了,问题在于首先这个计数类是一个公用的类,所有的线程都公用这一个计数类,当其中一个线程将值修改之后,其他线程并不知道,导致所有线程对于 count 字段的修改并没有完全作用在 count 字段上,然而线程本来就是并发的,当他们运行到需要修改 count 的时候,需要让他们一个一个作用在 count,就像过独木桥一样,所有线程都要一个一个走在桥上.
SafeCount
1 | public class SafeCount implements Count { |
这里千万不要认为是执行了 add() 又执行 remove() 所以有线程问题,就算线程里面只执行 add() 操作同样也会有线程问题,比如增加到100的时候突然切换线程,另外一根线程100->101,然后又切换回来,同样为100->101,所以其中的一次加就被省略了,因为切换的那次加法没有作用在切换后的线程中.