发布与逃逸

发布和逸出

1 发布

发布一个对象是指使对象能够在当前作用域之外的代码中使用。

1.1 将对象引用保存在静态变量中

1.2 把对象传递给外边方法

1.3 发布一个内部的类实例

this引用在构造函数中逸出。
当且仅当对象的构造函数返回时,对象才构建完成,才处于一个可预测的和一致的状态。
常见错误

  1. 在构造函数中添加listener
  2. 在构造函数中启动线程
  3. 在构造函数中调用一个可改写的实例方法
    这种情况下,可以使用一个私有的构造函数和一个公共的工厂方法,将其分解为多步骤,从而避免不正确的构造过程。

1.4 发布一个对象时,间接发布对象

当发布一个数据或集合时,其中所包含的对象也一并发布。

1.5 发布对象的变量或方法可达的其他对象

2 安全发布

安全发布,包括对象引用的安全发布和对象状态的安全发布,两者必须同时满足,才算正在的安全发布。及对象的引用和对象的状态必须同时对其他线程可见。
即使某个对象的引用对其他线程是可见的,也不意味着对象的状态对使用该对象的线程来说一定是可见的。

2.1 不可变对象与初始化安全性

由于不可变对象是一种非常重要的对象,因此java内存模型为不可变对象的共享提供了一种特殊的初始化安全性保证。
即使在发布不可变对象的引用时没有使用同步,也仍然可以安全的访问该对象的状态。
同时在没有额外同步情况下,也可以安全的访问final类型的域,然而如果final类型的域所指的是可变对象,那么在访问这些域所指的对象状态时需要同步。

volatile+不可变对象。

2.2 安全发布常用模式

2.2.1 静态初始化函数中初始化对象

静态初始化由jvm在类的初始化阶段执行(classLoader)。

2.2.2 volatile或AtomicReferance

将对象的引用保存到volatile类型的域或AtomicReferance对象中。

2.2.3 final类型域

将对象的引用保存到某个正确构造对象的final类型域中。

2.2.4 锁保护

将对象的引用保存到一个由锁保护的域中。

2.2.5 线程安全的容器类
  1. 将键或值放到Hashtable、synchronizedMap或ConcurrentMap中
  2. 将元素放在Vector、CopyOnWriteArrayList、CopyOnWriteArraySet、synchronizedList或synchronizedSet中
  3. 把元素放在BlockingQueue或ConcurrentLinkedQueue中
  4. Future或Exchanger

2.3 事实不可变对象

如果对象在发布后不会被修改,那么对于其他在没有额外同步的情况下安全的访问这些对象的线程来说,安全发布是足够的。
如果对象从技术上是可变得,但其状态在发布后不会再改变,那么把这种对象称为“事实上不可变对象”。
在没有额外同步的情况下,任何线程都可以安全的使用被安全发布的事实不可变对象。

2.4 可变对象

如果对象在构建后可以修改,那么安全发布只能保证“发布当时”状态的可见性。
对于可变对象,不仅在发布对象时需要使用同步,而且在每次对象访问时同样需要使用同步来确保后续修改操作的可见性。

2.5 汇总

  1. 不可变对象可以通过任意机制来发布
  2. 事实不可变对象必须通过安全方式来发布
  3. 可变对象必须通过安全方式来发布,并且必须是线程安全的或者由某个锁保护起来。

3 逸出

当一个不应该被发布的对象被发布时,这种情况成为逸出

wenxinzizhu wechat
扫一扫,添加我的微信,一起交流共同成长(备注为技术学习)