对象的组合

简介: 对象的组合

1 设计线程安全的类

设计线程安全类的过程中,需要包含以下三个基本要素:


  • 找出构成对象状态的所有变量
  • 找出约束状态变量的不变性条件
  • 建立对象状态的并发访问管理策略


对象的状态


  • 所有的域都是基本类型,则这些域构成对象的全部状态
  • 包含其他对象,该对象的状态将包括被引用对象的域


同步策略

规定了如何将不变性条件、线程封闭和加锁机制结合起来以维护线程的安全性,并且规定了哪些变量由哪些锁来保护

1.1 收集同步需求

final类型的域使用的越多,越能简化对象可能状态的分析过程.


  • 不变性条件:判断状态是否是有效的
  • 后验条件:判断状态转换是否是有效的


由于上述二条件施加的各种约束,因此就需要额外的同步与封装.


  • 如果某些状态是无效的,必须对底层的状态变量进行封装.


1.2 分析依赖状态的操作


  • 先验条件:基于状态
  • 依赖状态:包含先验条件的操作


  • 单线程程序:无法满足先验条件,只能失败
  • 并发程序:先验条件可能因为其他线程的执行而变成真,因此要一直等待先验条件为真再执行该操作

1.3 分析状态的所有权

所有权在Java中只是一个类设计中的要素,在语言层面没有明显的表现.所有权意味着控制权,如果发布了某个可变对象的引用,则意味着共享控制权.在定义哪些变量构成对象的状态时,只考虑对象拥有的数据.

2 实例封闭

将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁.


被封闭的对象一定不能超过它们既定的作用域.

对象可以封闭在类的一个实例(eg.私有成员)中,或者封闭在某个作用域内(eg.局部变量),再或者封闭在线程内.


实例封闭是构建线程安全类的一个最简单方式,还使得不同的状态变量可以由不同的锁来保护.

Java的包装器工厂(eg. Collections.synchronizedList.etc),只要包装器对象拥有对底层容器对象的唯一引用(即把底层容器对象封闭在包装器中),那么它就是线程安全的。对底层容器对象的所有访问必须通过包装器来进行。

当发布其他对象时,例如迭代器或内部的类实例,可能会间接地发布被封闭对象,同样会使被封闭对象逸出。

封闭机制更易于构造线程安全的类,因为当封闭类的状态时,在分析类的线程安全性时就无须检查整个程序

Java监视器模式

遵循Java监视器模式的对象会把对象的所有可变状态都封装起来,并由对象自己的内置锁来保护.

Java监视器模式的主要优势在于它的简单性.

3 线程安全性的委托

3.1 独立的状态变量

多个变量之间是彼此独立,则可将线程安全性委托给多个状态变量.

即组合成的类不会在其包含的多个状态变量上增加任何不变性条件.

3.2 当委托失效时

如果某个类含有复合操作,那么仅靠委托不足以实现线程安全性。在这种情况下,这个类必须提供自己的加锁机制以保证这些复合操作都是原子操作,除非整个复合操作都可以委托给状态变量。

3.3 发布底层的状态变量

如果一个状态变量是线程安全的,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,那么就可以安全地发布这个变量。

4 在现有的线程安全类中添加功能

4.1 客户端加锁机制

对于由Collections.synchronizedList封装的ArrayList,扩展类的功能,但并不是扩展类本身,而是将扩展代码放入一个”辅助类”中.

如下实现了一个包含”若没有则添加”操作的辅助类,用于对线程安全的List执行操作,但其中的代码是错误的.

@NotThreadSafe
class BadListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());
    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !list.contains(x);
        if (absent)
            list.add(x);
        return absent;
    }
}

要使其正确执行,必须使List在实现客户端外锁时使用同一个锁.

客户端加锁是指:对于使用某个对象的X的客户端代码,使用X本身用于保护其状态的锁来保护这段客户代码.要使用客户端加锁,必须知道对象X使用的哪一个锁.


目录
相关文章
|
API Serverless 监控
函数组合的N种方式
随着以函数即服务(Function as a Service)为代表的无服务器计算(Serverless)的广泛使用,很多用户遇到了涉及多个函数的场景,需要组合多个函数来共同完成一个业务目标,这正是微服务“分而治之,合而用之”的精髓所在。
2350 0
|
4月前
|
Java
继承与组合的区别
【8月更文挑战第22天】
84 0
|
6月前
|
数据安全/隐私保护 C++
C++ 中的类是一种用户定义的数据类型,用于表示具有相似特征和行为的对象的模板。
C++ 中的类是一种用户定义的数据类型,用于表示具有相似特征和行为的对象的模板。
|
7月前
|
定位技术 计算机视觉 Windows
类间两种关系:继承、 组合
类间两种关系:继承、 组合
37 0
|
7月前
2020-10-10 数组和对象的区分方法
2020-10-10 数组和对象的区分方法
|
7月前
|
C++
4. C++类的组合
4. C++类的组合
58 0
|
算法 C语言 C++
C++类的组合练习
C++类的组合练习
|
算法 C++ 容器
关系类算法函数
关系类算法函数
|
编译器 C语言 C++
C++ 之什么是类 & 对象的关系?
C++ 之什么是类 & 对象的关系?