4.2策略化加锁(Strategized Locking)
1.问题
运行在多线程环境中的组件必须保护其临界区不被客户机并发访问。同步机制与组件功能的集成需要解决以下两个强制条件:
1)不同的应用程序可能要求不同的同步策略,如互斥、读写锁或信息灯。因此,应该可以按照具体应用的需求定制组件的同步机制。
2)加入新的功能和隐错修正应很容易。特别为避免“版本混乱”应始终将这些变化一致地自动地应用于组件系列的所有成员上。
2.解决方案
将组件的同步特性变成“可插”的类型,用这种方式将组件的同步特性参数化。每种类型将特定的同步化策略对象化,同步策略包括互斥、读写锁、信号灯或“空”锁等。将这些可插的类型的实例定义为包含在组件中的对象,该组件可以使用这些对象有效地使其方法实现同步化。
3.实现
1)不考虑组件的同步特性,定义组件的接口和实现。
2)将加锁机制策略化。许多组件有相对简单的同步特性,可以使用常用的加锁策略,如互斥和信号灯等实现这些同步特性。可以统一地使用多态性或参数化类型将同步特性策略化。一般来说,如果直到运行时才知道加锁策略,就应该使用多态性。相反,如果在编译时就知道加锁策略,那就应该使用参数化类型。参数化类型有运行效率高的特点,而多态性有运行时可扩展的潜力。
假设使用定界加锁惯用法,策略化锁包括两个子活动:
2.1)为加锁机制定义一个抽象接口。为了使组件能使用不同的加锁机制,这些机制的所有具体实现应该使用具有共同特征的抽象接口,用于在多态性或参数化类型的基础上获取和释放锁。
2.2)使用定界加锁惯用法定义一个用同步特征将其策略化的哨兵类。这种设计遵循策略模式。其中,哨兵类作为拥有某一特殊锁的语境,而具体锁提供策略。
3)更新组件的接口和实现。在策略化同步机制后,组件可以使用这些机制,通过显式地获得或释放一个锁,或者使用定义的哨兵类来保护临界区。
4)修改组件实现以避免死锁和删除不必要的加锁开销。如果组件间存在方法调用,那么开发人员必须仔细地设计其组件实现,以避免自死锁和不必要的同步开销。
5)定义一组具有统一接口的加锁策略,这些策略可以支持各种应用特定的并发设计。
4.结论
优点:
1)增加灵活性和个性化。为特定的并发模型配置和定制一个组件是很容易的,因为组件的同步特性被策略化了。如果对于一种新的并发模型没有合适的加锁策略可用,可以在不影响已有代码的情况下扩充新的加锁策略。
2)降低组件维护的代价。使用策略化加锁模式很容易改进组件和隐错修正,因为对于各种并发模型只有一个实现,而不是每种并发模型对应一个独立的实现。将问题集中的方法有助于减少版本混乱。
3)改善重用性。用这种模式实现的组件不太依赖于具体的同步机制。
不足:
1)强行(obtrusive)加锁。
2)过分工程化(over-engineering)。