使一个对象能够被当前范围之外的代码所使用的叫做发布对象
import java.util.Arrays; /** * 多线程的场景下无法保证其他线程对states的修改,所以这个对象是线程不安全的 */ public class PublishNotSafe { private String[] states = {"a", "b", "c"}; public String[] getStates() { return states; } public static void main(String[] args) { PublishNotSafe p = new PublishNotSafe(); System.out.println("1-" + Arrays.toString(p.getStates())); // 1-[a, b, c] p.getStates()[0] = "修改之后的值"; System.out.println("2-" + Arrays.toString(p.getStates())); // 2-[修改之后的值, b, c] } }
那么如何保证对象发布的安全性呢,有以下四种方法
在静态初始化函数中初始化一个对象引用
将对象的引用保存到volatile类型或者AtomicReference对象中
将对象的引用保存到某个正确构造对象的final类型域中
将对象的引用保存到一个由锁保护的域中
下面以单例模式来说明如何安全发布对象
/** * 饿汉式单例,线程安全 * 但是每次类初始化的时候就执行构造方法,可能会导致性能下降 */ public class SingleHungry { private static final SingleHungry SINGLE_HUNGRY = new SingleHungry(); private SingleHungry() { } public static SingleHungry getSingleHungryInstance() { return SINGLE_HUNGRY; } }
/** * 懒汉式单例,线程不安全 * 调用的时候才会发生初始化,性能略高 */ public class SingleLazy { private static SingleLazy SINGLE_LAZY ; private SingleLazy() { } public static SingleLazy getSingleLazyInstance() { // 问题出现在这里,当多线程的场景下没有保证线程安全 if (SINGLE_LAZY == null) { SINGLE_LAZY = new SingleLazy(); return SINGLE_LAZY; } return SINGLE_LAZY; } }
/** * 双检锁 */ public class SingleDoubleCheckLock { private static SingleDoubleCheckLock instance; private SingleDoubleCheckLock() { } // 1.memory = allocate() 分配对象的内存空间 // 2.ctorInstance() 初始化对象 // 3.instance = memory 设置instance指向刚分配的内存 // JVM和CPU进行优化,发生了指令重排序 // 1.memory = allocate() 分配对象的内存空间 // 3.instance = memory 设置instance指向刚分配的内存 // 2.ctorInstance() 初始化对象 public static SingleDoubleCheckLock getInstance() { if (instance == null) { // 双重检测机制 // B线程执行到这里的时候发现A已经执行到new对象的步骤直接return了一个没有指针的对象 synchronized (SingleDoubleCheckLock.class) { // 同步锁 if (instance == null) { instance = new SingleDoubleCheckLock(); // A线程 - 3 } } } return instance; } }
/** * 双检锁 */ public class SingleDoubleCheckLock { private static volatile SingleDoubleCheckLock instance; private SingleDoubleCheckLock() { } // 1.memory = allocate() 分配对象的内存空间 // 2.ctorInstance() 初始化对象 // 3.instance = memory 设置instance指向刚分配的内存 public static SingleDoubleCheckLock getInstance() { if (instance == null) { synchronized (SingleDoubleCheckLock.class) { if (instance == null) { instance = new SingleDoubleCheckLock(); } } } return instance; } }
public class SingleEnum { public static void main(String[] args) { SingleEnum s1 = SingleEnum.getInstance(); SingleEnum s2 = SingleEnum.getInstance(); System.out.println(s1); System.out.println(s2); } private SingleEnum() { } public static SingleEnum getInstance() { return Singleton.INSTANCE.getInstance(); } private enum Singleton { INSTANCE; private SingleEnum singleton; // JVM保证这个方法只会被调用一次 Singleton() { singleton = new SingleEnum(); } public SingleEnum getInstance() { return singleton; } } }
一种错误的发布。当一个对象还没有构造完成时,就使它被其他线程所见
/** * 在EscapeNotSafe没有被正确构造完成时就已经被InnerClass引用到了,可能会出现问题 * thread2在EscapeNotSafe没有完全发布之前就已经看到它了 * 不应该直接new InnerClass(),应该采用专有的init方法 */ public class EscapeNotSafe { private int thisCanBeEscape = 0; public EscapeNotSafe() { // thread2 new InnerClass(); } private class InnerClass { public InnerClass() { System.out.println(EscapeNotSafe.this.thisCanBeEscape); } } public static void main(String[] args) { // thread1 new EscapeNotSafe(); } }