安全发布对象
一、对象发布
在介绍安全发布对象之前,应该首先聊一聊什么是发布对象。发布对象是“使一个对象能够被当前范围之外的代码所使用“
比如是下面三种方式
1.将一个指向该对象的引用保存到其他代码可以访问的地方
public class Student(){
private String name;
private int age;
}
public class Record{
Student s;
}
2.在某一个非私有的方法中返回该引用
public class Student(){
private String name;
private int age;
public Student getStudent(){
return new Student();
}
}
3.将引用传递到其他类的方法
//情况三
public class Student(){
private String name;
private int age;
}
public class Record{
private void getStudentInfo(Student s){
.....
}
}
但是发布对象,可能会牵扯到不应该发布的对象被发布的情况,这种现象称为对象逸出。
二、对象逸出
常见的对象逸出有
1.内部的可变状态逸出
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
@Slf4j
public class UnsafePublish {
private String[] states = {"a","b","c"};
public String[] getStates(){
return states;
}
public static void main(String[] args) {
UnsafePublish unsafePublish = new UnsafePublish();
log.info("{}", Arrays.toString(unsafePublish.getStates()));
unsafePublish.getStates()[0] = "d";
log.info("{}", Arrays.toString(unsafePublish.getStates()));
}
}
数组states本身是类的私有的变量,但是以public公有的方式发布出去,导致states已经逸出了它所在的作用域,任何调用者都能修改这个数组的内容。这在多线程环境下是危险的。
2.隐式地使this引用逸出
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Escape {
private int thisCanBeEscape = 1;
public Escape() {
new InnerClass();
}
private class InnerClass {
public InnerClass() {
log.info("{}",Escape.this.thisCanBeEscape);
}
}
public static void main(String[] args) {
new Escape();
}
}
运行结果:
这个类的内部类的实例里面隐含了对外部类的引用。这样在外部类对象没有完全正确构造之前,就会被发布,有可能有不安全的因素在里面,this引用有逸出的风险,比如说thisCanBeEscape=1 还没执行,这个时候的值就是0 。解决的话可以采用工厂方法
止this引用在构造函数过程中逸出 或者私有构造函数
完成对象创建或者监听器的注册等。
三、安全发布的方法
可变对象必须通过安全的方式来发布,这通常意味着在发布和使用该对象的线程时都必须使用同步。
要安全地发布一个对象,对象的引用以及对象的状态必须对其他线程可见。一个正确构造的对象可以通过以下方式来安全地发布:
- 在静态初始化函数中初始化一个对象引用
private staits Student s= new Student();
- 将对象的引用保存到 volatile类型域或者 AtomicReference对象中
- 将对象的引用保存到某个正确构造对象的final类型域中
- 将对象的引用保存到一个由锁保护的域中
1和2、4的说法可以参考相关的单例模式,这有之前写的文章【Java设计模式】单例模式,这个写的是比较全面的。
《Java并发编程实战》这本书在这部分的内容,提到在线程安全容器内(如Vector或者synchronizedList等)的同步意味着,将对象放入某个容器,线程安全库的容器类提供了很多的安全发布保证。类库中的其他数据传递机制(例如Future和Exchanger)同样能实现安全发布。
最后需要说明一下:本篇文章参考了《Java并发编程实战》,含有部分内容的引用。
这本书不适合入门,这篇文章能帮助你读懂安全发布对象这一小节了。