Java编发编程第三章 共享对象
3.1.3 锁与可见性
- 64位数值(如long/double)在并发时会出现问题
3.1.4 易变变量
- 加锁的意义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读和写操作的线程都应该在同一个锁上同步。
- 当一个变量被声明为
volatile
时,编译器和运行时会注意到这是一个共享变量,并且对它的操作不应该与其他内存操作一起被重新排序。 - 从内存的角度,写入
volatile
变量就像是退出了同步块,而读取一个volatile
变量就像进入了同步块。 - 不建议过度依赖
volatile
变量提供的可见性,这通常比使用锁更脆弱而且更难以理解。 - 只在能够简化代码实现和并发策略验证时使用
volatile
,如果验证正确性时需要对可见性进行微妙推理时,就要避免使用volatile
。 volatile
的正确使用方法包括:1.保证自身可见性。2.保证他们指向对象的可见性。3.指示重要声明周期(初始化或者关闭)事件的发生。- 调试提示: 对于服务器程序,无论是开发还是测试阶段启动JVM都一定要指定
-server
选项典型应用
使用volatile
变量作为标志,只是循环的结束。最佳实践
当且仅当满足以下条件时,才应该使用volatile
变量
- 对变量的写入不依赖于变量的当前值,或者你能保证只有单个线程更新变量的值。
- 该变量不会与其他变量一起纳入不可变性条件当中。
- 在访问变量时不需要加锁。
3.2 发布和逸出
逸出: 当发布了一个不该被发布的对象时,这种情况就叫逸出(escape
)
3.5 安全发布
不可变对象所需要的三个条件:
- 对象的状态不可改变
- 所有的
field
都是final
类型- 正确的构造过程
- 任何线程都可以在不需要额外同步的情况下安全的访问不可变对象,即使在发布这些对象时没有使用同步。
- 尽管在构造函数中设置的域值貌似是对该值的第一次写入,因此不会有“更旧的”的值被视为失效值,但实际上Object的构造函数会在子对象的构造函数执行前先将所有域设置一遍默认值,因此某个域的值可能被视为失效值。
3.5.3 安全发布的常用模式
可变对象必须通过安全方式发布,这通常意味着发布和使用该对象的线程都需要使用同步。
一个正确构造的对象可以通过以下几种方式安全地被发布: - 在静态初始化函数中初始化一个对象的引用
- 将该对象的引用保存到
volatile
类型的域或者AtomicReferance
对象中 - 将该对象的引用保存到一个被正确构造对象的
final
域中 或 - 将该对象的引用保存到一个由正确的锁保护的域中
3.5.4 事实不可变对象
3.5.5 可变对象
对象得发布需求取决于它的可变性:
- 不可变对象可以通过任意方式发布
- 事实不可变对象需要同步安全方式发布
- 可变对象必须同过安全发布,并且是线程安全的或者由某个锁保护起来
第四章 对象的组合
4.1 设计线程安全的类
在设计线程安全类的过程中,需要包含以下三个基本要素
- 找出构成对象状态的所有变量
- 找出约束状态变量的不变性条件
- 建立对象状态的并发访问策略