使用实例封闭的方式实现线程安全
将数据封装在对象的内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的值(我的理解是将数据封闭在对象或者某个线程的内部,不发布出去,使得其他线程不能够对该数据直接进行访问,从而保证数据对外部的安全性。封闭数据的另一个好处,在安全性出现问题时,也不需要去检查整个程序,只需要检查单个类就行了)。
简单的封闭例子
Java监视器模式(一开始想成了观察者模式。synchronized被称为监视器,该模式是利用synchronized来实现线程安全)
车辆追踪的代码1
可变的point
此种写法是有问题的,因为MutablePoint的x,y是可变的。所以在每次返回MutablePoint的时候都要新建一个MutablePoint对象。每次新建对象都会消耗大量的时间,这样将极大的降低系统的性能。但是如果需求需要保持内部一致性(个人猜想:保持一致性是因为强行加了锁,而synchronized会保持整个数据的一致性)
线程的安全性委托
车辆追踪代码2
不变的Point
委托的车辆追踪,可能会出现数据一致性的问题(原因当时想了很久,发现可能是数据结构的问题。所以百度了下,果然是因为其使用委托的安全结构ConcurrentHashMap类具有弱一致性,具体详见https://blog.csdn.net/wzq6578702/article/details/50908836),但是其省去了车辆追踪代码1非常多的对象创建所浪费的时间,从而更有利于多个线程频繁的去获取车辆位置的快照
当需要一个不发生变化的车辆视图,那么只需要一个Map的浅拷贝就行。使用HashMap的袁颖是因为在这个方法中不需要保证返回一个线程安全的map
独立的状态变量
前面的示例中,都是委托了单个线程安全的状态变量。如果委托了多个线程安全的状态变量,可能会造成委托失效。造成委托失效的原因就是因为这些委托的安全变量没有相互的独立,既组合而成的类会在其包含的多个状态变量上增加某一种不变性条件(这多个委托的安全变量相互影响)。
发布底层的状态变量
当要考虑是否要发布某个状态变量时,需要考虑该变量的不变性条件。
发布状态下的车辆追踪器
安全的point
在该种实现方式中,客户可以获取某一车辆位置,并且可以进行修改并且达到实时更新的效果,但是如果他在车辆位置的有效值上施加了任何约束,那么就不再安全。如果要对车辆位置的变化进行判断或者车辆位置的变化进行一些操作。
现有安全类增加功能(向集合中增加若没有则添加)
扩展方法对vector进行改造
直接将一个方法编写在需要的类的同一个源文件中,是最容易理解与维护的。当然对于jdk提供的api我们是不能修改源码的。那么我们需要对该种jdk内置类进行扩展
扩展方法比直接在添加到类中要更加脆弱,因为现在的同步策略实现被分布到多个单独维护的源代码中,如果底层代码做了修改,采用了另外一种锁来进行同步,那么BetterVector的安全性将被破坏。(Vector的规范中定义了他的同步策略,所以BetterVector不存在这个问题)
客户端机制加锁
但是对于Collections.synchronizedList封装的ArrayList,使用扩展法和直接法都无效。因为扩展法不知道他到底返回的是什么类型。第三种方法就是扩展类的功能
这种实现方式乍一看没有任何的问题,但是认真看就能看出错误。可以发现该辅助类使用的锁绝不和list中使用的锁是不同的。这将产生安全性问题(当调用List中的方法,又进行putIfAbsent方法,此时就不会同步运行,从而造成危险)
通过客户端加锁来实现该功能。
这样就解决了前面的问题。但是客户端加锁的方式也是非常脆弱的。因为底层结构的锁放到了一个毫不相关的类中。
组合方式
将某个对象通过参数传递给ImprovedList后,客户端直接使用ImprovedList来对数据进行存储。那么不管底层的list加锁方式如何改变,ImprovedList都能够适应变化(因为加锁策略是采用自己的策略,并不是依据底层结构),虽然额外的同步层可能会造成轻微的性能损失,但是ImprovedList的健壮性加强。
重点
- 使用对象的私有锁对象,比使用对象的内置锁有很多好处
原因:1.1私有锁对象可以封装起来,是客户无法获得锁对象
1.2如果客户端错误的获得一个对象的锁,那么将产生活跃性问题。
1.3共有的锁对象在程序中是否被正确的访问,需要检查整个程序,私有只需要检查某个类
- Volatile变量的使用规则:仅当一个安全变量参与到包含其他状态变量的不变性条件时,才可以申明为volatile类型。(个人想法:这是因为当某个变量参与到不变性条件时,如果不声明为volatile类型,那么某个线程对该变量进行修改时,变量对其他线程的修改不可见,那么就会出现委托失效)
- 扩展方式和客户端机制加锁都会破坏同步策略的封装性
参考文献
《java并发编程实战》
《java代码优化》