高并发之——各种单例代码分析

如何安全的发布对象:
(1)在静态初始化函数中初始化一个对象引用
(2)将对象的引用保存到volatile类型域或者AtomicReference对象中
(3)将对象的引用保存到某个正确构造对象的final类型域中
(4)将对象的引用保存到一个由锁保护的域中
接下来,看几个单例对象的示例代码,其中有些代码是线程安全的,有些则不是线程安全的。
代码一:SingletonExample1
这个类是懒汉模式,并且是线程不安全的

**
 * @author 
 * @Title:
 * @Package
 * @Description: 懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程不安全的
 * @date 2020/7/1415:46
 */
public class SingletonExample1 {

    private SingletonExample1() {
    }

    private static SingletonExample1 instance = null;


    public static SingletonExample1 getInstance() {
        if (instance == null) {
            return new SingletonExample1();
        }
        return instance;
    }

}

代码二:SingletonExample2
饿汉模式,单例实例在类装载的时候进行创建,是线程安全的


/**
 * @author 
 * @Title:
 * @Package
 * @Description: 饿汉模式,单例实例在类装载的时候进行创建,是线程安全的
 * @date 2020/7/1415:53
 */
public class SingletonExample2 {

    private SingletonExample2() {
    }

    private SingletonExample2 intance = new SingletonExample2();

    private static SingletonExample2 getInstance() {
        return new SingletonExample2();
    }

}

代码三:SingletonExample3
懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程安全的,但是这个写法不推荐

/**
 * @author 
 * @Title:
 * @Package
 * @Description: 单例实例在第一次使用的时候进行创建,这个类是线程安全的,但是这个写法不推荐
 * @date 2020/7/1415:56
 */
public class SingletonExample3 {

    private SingletonExample3() {
    }

    private static SingletonExample3 instance = null;

    public static synchronized SingletonExample3 getinstance() {
        if (instance == null) {
            return new SingletonExample3();
        }
        return instance;
    }

}

代码四:SingletonExample4
懒汉模式(双重锁同步锁单例模式),单例实例在第一次使用的时候进行创建,但是,这个类不是线程安全的!!!!!

/**
 * @author 
 * @Title:
 * @Package
 * @Description:懒汉模式(双重锁同步锁单例模式)单例实例在第一次使用的时候进行创建,这个类不是线程安全的
 * @date 2020/7/1416:03
 */
public class SingletonExample4 {
    private SingletonExample4() {
    }

    private static SingletonExample4 instance = null;
    //线程不安全
    //当执行instance = new SingletonExample4();这行代码时,CPU会执行如下指令:
    //1.memory = allocate() 分配对象的内存空间
    //2.ctorInstance() 初始化对象
    //3.instance = memory 设置instance指向刚分配的内存
    //单纯执行以上三步没啥问题,但是在多线程情况下,可能会发生指令重排序。
    // 指令重排序对单线程没有影响,单线程下CPU可以按照顺序执行以上三个步骤,但是在多线程下,如果发生了指令重排序,则会打乱上面的三个步骤。

    //如果发生了JVM和CPU优化,发生重排序时,可能会按照下面的顺序执行:
    //1.memory = allocate() 分配对象的内存空间
    //3.instance = memory 设置instance指向刚分配的内存
    //2.ctorInstance() 初始化对象
    //假设目前有两个线程A和B同时执行getInstance()方法,A线程执行到instance = new SingletonExample4(); B线程刚执行到第一个 if (instance == null){处,
    //如果按照1.3.2的顺序,假设线程A执行到3.instance = memory 设置instance指向刚分配的内存,此时,线程B判断instance已经有值,就会直接return instance;
    //而实际上,线程A还未执行2.ctorInstance() 初始化对象,也就是说线程B拿到的instance对象还未进行初始化,这个未初始化的instance对象一旦被线程B使用,就会出现问题。
    public static SingletonExample4 getInstance() {

        if (instance == null) {
            synchronized (SingletonExample4.class) {
                if (instance == null) {
                    instance = new SingletonExample4();
                }

            }
        }
        return instance;
    }


}

线程不安全分析如下:
当执行instance = new SingletonExample4();这行代码时,CPU会执行如下指令:
1.memory = allocate() 分配对象的内存空间
2.ctorInstance() 初始化对象
3.instance = memory 设置instance指向刚分配的内存
单纯执行以上三步没啥问题,但是在多线程情况下,可能会发生指令重排序。
指令重排序对单线程没有影响,单线程下CPU可以按照顺序执行以上三个步骤,但是在多线程下,如果发生了指令重排序,则会打乱上面的三个步骤。

如果发生了JVM和CPU优化,发生重排序时,可能会按照下面的顺序执行:
1.memory = allocate() 分配对象的内存空间
3.instance = memory 设置instance指向刚分配的内存
2.ctorInstance() 初始化对象

假设目前有两个线程A和B同时执行getInstance()方法,A线程执行到instance = new SingletonExample4(); B线程刚执行到第一个 if (instance == null){处,如果按照1.3.2的顺序,假设线程A执行到3.instance = memory 设置instance指向刚分配的内存,此时,线程B判断instance已经有值,就会直接return instance;而实际上,线程A还未执行2.ctorInstance() 初始化对象,也就是说线程B拿到的instance对象还未进行初始化,这个未初始化的instance对象一旦被线程B使用,就会出现问题。

代码五:SingletonExample5
懒汉模式(双重锁同步锁单例模式)单例实例在第一次使用的时候进行创建,这个类是线程安全的,使用的是 volatile + 双重检测机制来禁止指令重排达到线程安全


/**
 * @Title:
 * @Package
 * @Description:懒汉模式(双重锁同步锁单例模式) 单例实例在第一次使用的时候进行创建,这个类是线程安全的
 * @author 
 * @date 2020/7/1416:55
 */
public class SingletonExample5 {

    private  SingletonExample5(){}
    //单例对象  volatile + 双重检测机制来禁止指令重排
    private volatile static SingletonExample5 instance = null;

    public static SingletonExample5 getInstance(){
        if (instance == null){
            synchronized (SingletonExample5.class){
                if(instance == null){
                    instance = new SingletonExample5();
                }
            }
        }
        return instance;
    }

}

代码六:SingletonExample6
饿汉模式,单例实例在类装载的时候(使用静态代码块)进行创建,是线程安全的


/**
 * @Title:
 * @Package
 * @Description:
 * @author 
 * @date 2020/7/14 16:59
 */
public class SingletonExample6 {
    private SingletonExample6(){}

    private static SingletonExample6 instance = null;

    static {
        instance = new SingletonExample6();
    }
    public static SingletonExample6 getInstance(){
        return instance;
    }

}

代码七:SingletonExample7
枚举方式进行实例化,是线程安全的,此种方式也是线程最安全的

/**
 * @author 
 * @Title:
 * @Package
 * @Description: 枚举方式进行实例化,是线程安全的,此种方式也是线程最安全的
 * @date 2020/7/14 17:11
 */
public class SingletonExample7 {

    private SingletonExample7() {
    }

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

    private enum Singleton {
        INSTANCE;
        private SingletonExample7 singleton;
        //JVM保证这个方法绝对只调用一次
        Singleton() {
            singleton = new SingletonExample7();
        }

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

你可能感兴趣的:(高并发之——各种单例代码分析)