单例模式与线程安全的理解

package com.example.concurrency.singleton;

import com.example.concurrency.annotations.NotThreadSafe;

/**
 * 懒汉模式
 * 单利实力在第一次使用时候进行创建
 */
@NotThreadSafe
public class SingletonExample1 {

    // 私有构造方法
    private SingletonExample1() {
    }

    // 单利对象
    private static SingletonExample1 singletonExample1 = null;

    // 静态工厂方法
    private static SingletonExample1 getInstance() {
        if (singletonExample1 == null) {
            singletonExample1 = new SingletonExample1();
        }
        return singletonExample1;
    }

    /*
    在多线程的情况下,上面这段代码,
        if (singletonExample1 == null) {
            singletonExample1 = new SingletonExample1();
        }
    很有可能该实例被new了两次。这样一旦在私有构造方法中有对资源进行操作,那么线程就会不安全
     */
}
package com.example.concurrency.singleton;

import com.example.concurrency.annotations.ThreadSafe;

/**
 * 恶汉模式
 * 单利实力在类转载时候创建
 */
@ThreadSafe
public class SingletonExample2 {

    // 私有构造方法
    private SingletonExample2() {
    }

    // 单利对象
    private static SingletonExample2 singletonExample1 = new SingletonExample2();

    // 静态工厂方法
    private static SingletonExample2 getInstance() {
        return singletonExample1;
    }

    /*
        使用饿汉模式要考虑两个问题,
        第一:私有构造函数在实现的时候,没有过多的操作需要处理
        第二:这个类,在实际的过程中,肯定会被使用,这样不会带来资源的浪费
     */
}
package com.example.concurrency.singleton;

import com.example.concurrency.annotations.NotRecommend;
import com.example.concurrency.annotations.ThreadSafe;

/**
 * 懒汉模式
 * 单利实力在第一次使用时候进行创建
 */
@ThreadSafe
@NotRecommend
public class SingletonExample3 {

    // 私有构造方法
    private SingletonExample3() {
    }

    // 单利对象
    private static SingletonExample3 singletonExample1 = null;

    // 静态工厂方法
    private static synchronized SingletonExample3 getInstance() {
        if (singletonExample1 == null) {
            singletonExample1 = new SingletonExample3();
        }
        return singletonExample1;
    }

    /*
        安全了,但是这种写法并不推荐,他随然保证了,同一时间内,只允许一个线程来访问这个方法,来保证了线程安全,
        但是,他却带来了性能上的开销
     */
}
package com.example.concurrency.singleton;

import com.example.concurrency.annotations.NotThreadSafe;

/**
 * 懒汉模式---双重同步锁单利模式
 * 单利实力在第一次使用时候进行创建
 */
@NotThreadSafe
public class SingletonExample4 {

    // 私有构造方法
    private SingletonExample4() {
    }

    // 单利对象
    private static SingletonExample4 singletonExample1 = null;

    // 静态工厂方法
    private static SingletonExample4 getInstance() {
        if (singletonExample1 == null) {
            synchronized (SingletonExample4.class) {
                // 使用了双重检测机制+同步锁
                if (singletonExample1 == null) {
                    singletonExample1 = new SingletonExample4();
                }
            }
        }
        return singletonExample1;
    }

    /*
        以上,是线程不安全的
        JVM和CPU优化,发生了指令重排序

        1、memory = allocate(); 分配对象内存空间
        2、ctorInstance() 初始化对象
        3、instance = memory 设置instance指向刚刚分配的内存空间

        重排序以后,有可能就不是按照1-2-3的顺序执行了,上面的步骤里 ,3 和2 并没有什么必然的先后顺序
        于是走了1-3-2的顺序

        线程A 还没有初始化对象,但是已经将instance指向了刚刚分配的内存,
        这时候,线程B进来一看 instance 有值了,于是直接返回instance
        然而其实此刻instance还没有被线程A new出来。
        这时候,一旦拿instance去调用方法执行某些操作,就会出现异常

     */
}
package com.example.concurrency.singleton;


import com.example.concurrency.annotations.Recommend;
import com.example.concurrency.annotations.ThreadSafe;

/**
 * 枚举模式,相比于饿汉模式在实际调用的时候才开始进行初始化,不会造成资源的浪费
 */
@ThreadSafe
@Recommend
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;
        }
    }
}

 

你可能感兴趣的:(并发编程)