5.1安全发布对象
名称解释
发布对象:使一个对象可以被当前范围之外的代码所使用。
对象溢出:一种错误的发布。当一个对象还没有构造完成时,就使他被其他线程所见
代码演示
错误的发布对象示例
package com.moluo.concurrency.publish;
import java.util.Arrays;
public class UnsafePublishExample {
private String[] states = {"a", "b", "c"}; // 使用private关键字修饰states变量,意图该变量无法被修改
public String[] getStates() {
return states;
}
public static void main(String[] args) {
UnsafePublishExample unsafePublishExample = new UnsafePublishExample();
System.out.println(Arrays.toString(unsafePublishExample.getStates()));
unsafePublishExample.getStates()[0] = "d"; // 但仍然被修改
System.out.println(Arrays.toString(unsafePublishExample.getStates()));
}
}
对象溢出示例
package com.moluo.concurrency.publish;
public class EscapeExample {
private int thisCanBeEscape = 0;
public EscapeExample() {
new InnerClass(); // 类EscapeExample的初始化需要InnerClass初始化完成
}
public static void main(String[] args) {
new EscapeExample();
}
private class InnerClass {
public InnerClass() {
System.out.println(EscapeExample.this.thisCanBeEscape); //内部类中使用了EscapeExample.this,即EscapeExample的对象。可这是类EscapeExample还未初始化完成
}
}
}
安全发布对象的四种方法
在
静态初始化函数中初始化
一个对象引用将对象的引用保存到
volatile类型域
或者AtomicReference对象中
将对象的引用保存到某个
正确构造函数对象的final类型域
中将对象的引用保存到一个由
锁保护的域中
下面我们通过单例来演示安全发布对象的四种方法
在静态初始化函数中初始化
一个对象引用
静态初始化函数中初始化
一个对象引用package com.moluo.concurrency.singleton;
import com.moluo.concurrency.annotation.ThreadSafe;
/**
* 饿汉模式
* 单例实例在类装载时进行创建
*
* @author moluo
* @since 2020/6/28
*/
@ThreadSafe
public class SingletonExample2 {
// 私有的构造函数
private SingletonExample2() {
}
// 单例对象
private static SingletonExample2 instance = new SingletonExample2();
// 静态的工厂方法
public static SingletonExample2 getInstance() {
return instance;
}
}
package com.moluo.concurrency.singleton;
import com.moluo.concurrency.annotation.ThreadSafe;
/**
* 懒汉模式
* 单例实例在第一次使用时进行创建
*
* @author moluo
* @since 2020/6/28
*/
@ThreadSafe
public class SingletonExample5 {
// 私有的构造函数
private SingletonExample5() {
}
/**
* 当静态代码块在静态属性之前时,发现getinstance()=null,什么原因造成的呢?
* 静态代码块先执行,生成一个SingletonExample5对象
* 静态属性后执行,将instance=null
* 大写的尴尬!!!
*
* 如何修正呢?
* 按照如下顺序书写代码,写静态属性,再静态块
* private static volatile SingletonExample5 instance = null;
*
* static {
* instance = new SingletonExample5();
* }
*
*/
static {
instance = new SingletonExample5();
}
// 单例对象
private static volatile SingletonExample5 instance = null;
// 静态的工厂方法
public static SingletonExample5 getInstance() {
return instance;
}
public static void main(String[] args) {
SingletonExample5 instance = SingletonExample5.getInstance();
System.out.println(instance);
}
}
将对象的引用保存到volatile类型域
或者AtomicReference对象中
volatile类型域
或者AtomicReference对象中
将对象的引用保存到一个由
锁保护的域中
package com.moluo.concurrency.singleton;
import com.moluo.concurrency.annotation.ThreadSafe;
/**
* 懒汉模式 双重同步锁单例模式
* 单例实例在第一次使用时进行创建
*
* @author moluo
* @since 2020/6/28
*/
@ThreadSafe
public class SingletonExample4 {
// 私有的构造函数
private SingletonExample4() {
}
/**
* 单例对象
* <p>
* 使用volatile的原因:防止指令重排序
*
* 我们已知,java初始化一个对象,需要以下三个步骤:
* 1、memory = allocate() 分配对象的内存空间
* 2、ctorInstance 初始化对象
* 3、instance = memory 设置instance指向刚分配的内存
* <p>
* 当JVM和cpu优化,可能发生如下指令重排:
* 1、memory = allocate() 分配对象的内存空间
* 3、instance = memory 设置instance指向刚分配的内存
* 2、ctorInstance 初始化对象
*
* 而这将造成
* 多线程情况下,假设有线程A、B,由于指令重排,当A执行完1、3两个步骤而未执行2时,对象还未初始化成功,
* 若此时线程B来获取对象,由于步骤3已经执行,线程B是可以获得对象引用的,但此刻对象未初始化完成,故该时刻线程B调用对象方法就会出现问题
*/
private static volatile SingletonExample4 instance = null;
// 静态的工厂方法
public static SingletonExample4 getInstance() {
if (instance == null) { // 双重检测机制
synchronized (SingletonExample4.class) { // 同步锁
if (instance == null) { // 双重检测机制
instance = new SingletonExample4();
}
}
}
return instance;
}
}
将对象的引用保存到一个由锁保护的域中
锁保护的域中
package com.moluo.concurrency.singleton;
import com.moluo.concurrency.annotation.NotRecommend;
import com.moluo.concurrency.annotation.ThreadSafe;
/**
* 懒汉模式
* 单例实例在第一次使用时进行创建
*
* @author moluo
* @since 2020/6/28
*/
@ThreadSafe
@NotRecommend
public class SingletonExample3 {
// 私有的构造函数
private SingletonExample3() {
}
// 单例对象
private static volatile SingletonExample3 instance = null;
// 静态的工厂方法
public static synchronized SingletonExample3 getInstance() {
if (instance == null) {
instance = new SingletonExample3();
}
return instance;
}
}
将对象的引用保存到某个正确构造函数对象的final类型域
中
正确构造函数对象的final类型域
中package com.moluo.concurrency.singleton;
import com.moluo.concurrency.annotation.NotThreadSafe;
import com.moluo.concurrency.annotation.Recommend;
import com.moluo.concurrency.annotation.ThreadSafe;
/**
* 懒汉模式
* 单例实例在第一次使用时进行创建
*
* @author moluo
* @since 2020/6/28
*/
@ThreadSafe
@Recommend
public class SingletonExample6 {
// 私有的构造函数
private SingletonExample6() {
}
// 单例对象
private static SingletonExample6 instance = null;
// 静态的工厂方法
public static SingletonExample6 getInstance() {
return Singleton.INSTANCE.getSingleton();
}
private enum Singleton {
INSTANCE;
private SingletonExample6 singleton;
// JVM保证枚举类的构造方法绝对只调用一次
Singleton() {
singleton = new SingletonExample6();
}
public SingletonExample6 getSingleton() {
return singleton;
}
}
}
Last updated
Was this helpful?