java 并发编程学习笔记(四)之 安全发布对象(单例模式实现几种实现方式)

                                          安全发布对象

错误发布对象:

发布对象:使一个对象能被当前范围之外的代码所使用

对象溢出:一种错误的发布,当一个对象还没有构造完成时,就使它被其他线程所见

如何 安全 发布对象呢 ?一般采用 以下四种方式

(1)首先来看一个 非安全发布对象代码:

@Slf4j
public class UnSafePublish {

    private String[] states = {"a", "b", "c"};

    /**
     * 无法确保 其他的线程会通过调用此方法,来修改私有的成员变量
     *
     * @return
     */
    public String[] getStates() {
        return states;

    }

    public static void main(String[] args) {
        UnSafePublish unSafePublish = new UnSafePublish();
        log.info("{}", Arrays.toString(unSafePublish.getStates()));

        unSafePublish.getStates()[0] = "d";
        log.info("{}", Arrays.toString(unSafePublish.getStates()));
    }
}

(2)非安全的 对象溢出

public class Escape {

    private  int thisCanBeEscape =0 ;
    /**一种错误的发布,当一个对象还没有构造完成时,就使它被其他线程所见*/
    public Escape() {
         new InnerClass();
    }

    private class  InnerClass{
        public  InnerClass(){
            log.warn("{}",Escape.this.thisCanBeEscape);
        }
    }
    public static void  main(String[] args){
        new Escape();
    }
}

 (3)单例模式的 几种方式比较

  • 懒汉模式
/**
 * 懒汉模式
 * 单例的实例在第一次使用的时候进行创建
 * <p>
 * 在单线程的环境下,没有问题,但是在多线程的环境下就会出问题。
 */
@NotThreadSafe
@Slf4j
public class SingletonExample1 {

    //私有构造函数
    private SingletonExample1() {

    }

    //单例对象
    private static SingletonExample1 instance = null;


    //静态的工厂方法
    //懒汉模式本身时线程不安全的,加上了synchronized关键字之后就可以实现安全
    //但是降低了性能,因此并不推荐这种写法
    public static  synchronized SingletonExample1 getInstance() {
        if (instance == null) {
            instance = new SingletonExample1();
        }
        return instance;
    }
}
  • 饿汉模式 
/**
 * 饿汉模式
 */
public class SingletonExample2 {


    private SingletonExample2() {

    }

    private static SingletonExample2 singletonExample2 = new SingletonExample2();

    public static SingletonExample2 getInstance() {
        return singletonExample2;
    }
}
  •  懒汉模式+ 双重检测同步锁
/**
 * 懒汉模式 -》》双重同步锁单例模式
 */
@NotThreadSafe
@Slf4j
public class SingletonExample3 {
    //私有构造函数
    private SingletonExample3() {

    }
    /**
     * 1.分配对象的内存空间
     * 2.初始化对象
     * 3.设置instance 指向刚分配的内存
     * 在单线程的情况下没有问题,但是在多线程的情况下
     *
     * jvm 和cpu 优化,发生指令重排
     *
     * 1.分配对象的内存空间
     * 2.设置instance 指向刚分配的内存
     * 3.初始化对象
     *
     * 因此我们需要限制SingletonExample3类的创建的时候的指令重排,添加volatile关键字
     */


    //单例对象
    //volatile 加上 双重检测机制,禁止指令重排
    private volatile static SingletonExample3 instance = null;


    //静态的工厂方法
    public static  SingletonExample3 getInstance() {
        if (instance == null) {     //双重检测机制
             synchronized (SingletonExample3.class) { //同步锁
                 if (instance == null) {
                     instance = new SingletonExample3();
                 }
             }
        }
        return instance;
    }
}
  •  饿汉模式 (静态块的方式)
/**
 * static 静态块的饿汉模式
 */
public class SingletonExample6 {
    private  SingletonExample6(){}

    private static SingletonExample6 instance =null;

    static  {
        instance = new SingletonExample6();
    }

    private  static  SingletonExample6 getInstance(){
        return instance;
    }
    
    public static void  main(String[] args){
        System.out.println(getInstance());
        System.out.println(getInstance());
    }
}
  • 枚举模式(最推荐) 
/**
 * 枚举模式,最安全
 * 利用枚举的特性,一个枚举的构造方法只会被调用一次实现单例模式,推荐使用这种方式
 * <p>
 * 优点:1、相比懒汉模式,更能保证线程安全性
 * 2、相比饿汉模式,也只会在第一次使用的时候进行创建实例,不会造成资源浪费
 */
@ThreadSafe
@Recommend
public class SingletonExample7 {


    private SingletonExample7() {
    }

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

    private enum Singleton {
        INSTANCE; //只有一个枚举变量,保证构造方法只调用一次


        private SingletonExample7 singletonExample7;

        //jvm保证这个方法绝对只调用一次
        Singleton() {
            System.out.println("我只会被调用一次");
            singletonExample7 = new SingletonExample7();
        }

        public SingletonExample7 getInstance() {
            return singletonExample7;
        }
    }

    public static void main(String[] args) {
        System.out.println(SingletonExample7.getInstance());
        System.out.println(SingletonExample7.getInstance());
        System.out.println(SingletonExample7.getInstance());
    }
}

猜你喜欢

转载自blog.csdn.net/qq_31905135/article/details/84227362
今日推荐