如何安全的发布对象?

定义

安全的发布对象: 在可变对象没有被构造完成之前不允许被其他线程所使用

饿汉模式发布对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * 饿汉模式
 */
public class SingletonExample2 {
    /**
     * 私有构造方法
     */
    private SingletonExample2(){

    }

    /**
     * 单例的对象
     * 在初始化需要较多操作的时候会比较耗时,程序启动的时候有性能问题
     * 如果不被使用可能会导致资源浪费
     */
    private static SingletonExample2 instance = new SingletonExample2();

    /**
     * 静态的工厂方法获取单例对象
     * @return
     */
    public static SingletonExample2 getInstance(){
        return instance;
    }
}

按需加载初始化对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
 * 双重同步锁检测机制
 * 单利模式,对象在第一次使用的时候创建
 */
public class SingletonExample5 {
    /**
     * 私有构造方法
     */
    private SingletonExample5(){

    }

    /**
     * 单例的对象
     * 使用volatile关键字限制指令重排序,
     */
    private volatile  static  SingletonExample5 instance = null;

    /**
     * 对象创建的过程
     * 1. memory = allocate() 分配对象空间
     * 2. newInstance() 初始化对象
     * 3. instance = memory 设置instance指向刚分配的内存
     * jvm 和cpu 优化发生了指令重排序, 对象创建 顺序变成 1 3 2,会导致未初始化完成的对象被外部使用.尤其是在初始化对象操作比较漫长的时候
     * volatile关键字的作用就是保证对象创建过程是 1 2 3
     * 静态的工厂方法获取单利对象
     * @return
     */
    public static SingletonExample5 getInstance(){
        if (instance == null){
            // 双重检测机制
            synchronized(SingletonExample5.class){
                if (instance == null){
                    instance = new SingletonExample5();
                }
            }
        }
        return instance;
    }
}

使用枚举发布对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
 * 基于枚举实现的单例模式
 * 线程安全的懒汉模式,只有被实际调用的时候才会被初始化,jvm 保证
 */
public class SingletonExample7 {

    private SingletonExample7(){

    }

    public static SingletonExample7 getInstance(){
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton {

        INSTANCE;

        private SingletonExample7 singleton;

        /**
         * jvm 保证这个方法被调用一次,并且只有在实际调用的时候才被初始化
         */
        Singleton() {
            singleton = new SingletonExample7();
        }

        public SingletonExample7 getInstance() {
            return singleton;
        }
    }
}