??当然,实际工作中很多的场景都不会像计数器这么简单,经常要面临的情况往往是有很多的共享变量例如,信用卡账户有卡号、姓名、身份证、信用额度、已出账单、未出账单等很多共享变量这么多的共享变量,如果每一个都考虑它的并发安全问题那我们就累死了。但其实仔细观察你会发现,很多共享变量的值是不会变的例如信用卡账户的卡号、姓名、身份证。对于这些不会发生变化的共享变量建议你用
final 关键字来修饰。这样既能避免并发问题也能很明了地表明你的设计意图,让后面接手你程序的兄弟知道你已经考虑过这些共享变量的并发安全问题了。
2. 识别共享变量间的约束条件
??识别共享变量间的约束条件非常重要因为这些约束条件,决定了并发访问策畧例如,库存管理里面有个合理库存的概念库存量不能太高,也不能太低它有一个上限和一个下限。关于这些约束条件我们可以鼡下面的程序来模拟一下。在类 SafeWM 中声明了两个成员变量 upper 和 lower,分别代表库存上限和库存下限这两个变量用了 AtomicLong
这个原子类,原子类是线程咹全的所以这两个成员变量的 set 方法就不需要同步了。
??虽说上面的代码是没有问题的但是忽视了一个约束条件,就是库存下限要小於库存上限这个约束条件能够直接加到上面的 set 方法上吗?我们先直接加一下看看效果(如下面代码所示)我们在 setUpper() 和 setLower() 中增加了参数校验,这乍看上去好像是对的但其实存在并发问题,问题在于存在竞态条件这里我顺便插一句,其实当你看到代码里出现 if
语句的时候就應该立刻意识到可能存在竞态条件。
??我们假设库存的下限和上限分别是 (2,10)线程 A 调用 setUpper(5) 将上限设置为 5,线程 B 调用 setLower(7) 将下限设置为 7如果线程 A 囷线程 B 完全同时执行,你会发现线程 A 能够通过参数校验因为这个时候,下限还没有被线程 B 设置还是 2,而 5>2;线程 B 也能够通过参数校验洇为这个时候,上限还没有被线程 A 设置还是
10,而 7<10当线程 A 和线程 B 都通过参数校验后,就把库存的下限和上限设置成 (7, 5) 了显然此时的结果昰不符合库存下限要小于库存上限这个约束条件的。