重用现有的类而不是创建新的类,可以降低工作量,开发风险以及维护成本。
有时候线程安全类可以支持我们所有的操作,但更多时候,现有的了类只能支持大部分的操作,此时就需要在不破坏线程安全性的情况下添加一个新的操作。
这里的例子:需要一个线程安全的链表,需要提供一个原子的“若没有则添加(Put-If-Absent)”的操作。
同步的List类已经实现了大部分功能,可以根据contains方法和add方法构造一个“若没有则添加”的操作。
修改原始类:不现实
另一个方法:继承这个类,但是继承比直接添加源码到类中更加脆弱,因为现在的同步策略实现被分布到了多个单独维护的源码中。如果底层的类改变了同步策略并选择了不同的锁来保护它的状态变量,那么子类会被破坏。
客户端加锁机制
第三种方法:扩展类的功能,不是直接继承类本身,而是将扩展代码放入一个“辅助类”中。
这里要注意,辅助类的锁和底层对象的锁是有区别的,到时候不要用错了锁。不然同步的方法看上去是加锁了,但是实际上只是假象,由于使用了不同的锁会导致底层对象被其他线程所修改(走的并不是你这个辅助类的加锁方法的代码路径)。
要使方法正确执行,必须使List在实现客户端加锁或者外部加锁使用的是同一把锁。
客户端加锁是指,对于使用某个对象X的客户端代码,使用X本身用于保护其状态的锁来保护这段客户端代码。
还有种情况?
客户端加锁更为脆弱,因为它将类C的加锁代码放到与C完全无关的其他类中。
组合
客户端加锁和继承加锁的方式都很脆弱,一旦有人不遵循加锁策略很可能不安全。
组合方法可以通过自身的内置锁加了一层额外的加锁,而不用关心底层的List是否是线程安全的。