volatile关键字与单例

单例分为恶汉模式和懒汉模式

1、恶汉模式,代码:

public class SingletonHungry {

private static SingletonHungryobj =new SingletonHungry();

    //将构造方法私有化,防止外部使用构造方法进行创建实例

    private SingletonHungry() {

}

public static SingletonHungrygetInstance(){

return obj;

    }

}


2、懒汉模式,代码:

public class SingletonLazy {

private static volatile SingletonLazyobj;

    private SingletonLazy(){

    //防止外界使用构造方法创建实例

    }

/**

    * 问题:多线程并发时,可能会创建多个obj对象

    * @return

    */

    public static SingletonLazygetInstance1(){

         if(obj ==null){

                 obj =new SingletonLazy();

         }

         return obj;

    }

/**

    *@description: 问题:当线程a执行到mark1时,线程b执行到synchronized块内,此时obj为null

    *  线程b会创建一个obj对象,同时线程a由于进入if代码块时以为obj为null,所以也将创建一个对象

    *  此时会创建多个obj对象

*/

    public static SingletonLazygetInstance2(){

          if(obj ==null){

               //mark1

                synchronized (SingletonLazy.class) {

                     obj =new SingletonLazy();

                }

           }

           return obj;

    }

/**

    * 也有问题:多线程时,当a线程进入synchronized代码块时,由于obj = new SingletonLazy()会分三步进行

    * 1、obj分配内存空间地址

    * 2、创建SingletonLazy.class对象的实例

    * 3、将步骤2创建的实例对象指向步骤1的内存空间地址

    * 由于步骤1的时候obj已经!=null了,此时b线程进入则直接返回obj对象,此时obj还没有创建完成。

    * 解决办法是在obj对象前加一个修饰符volatile,此处用到了volatile的防止指令重排的功能

    * @return

    */

    public static SingletonLazygetInstance3(){

         if(obj ==null){

               synchronized (SingletonLazy.class) {

                      if(obj ==null) {

                             obj =new SingletonLazy();

                       }

                }

        }

               return obj;

    }

}


注:

1、指令重排的含义:对于非原子性的操作,在不影响最终结果的情况下,其拆分成的原子操作可能会被重新排列执行顺序,以提高计算机的执行效率。

2、volatile禁止指令重排,用volatile修饰后的obj,对它的写操作就会添加一个内存屏障,在写操作完成之前不会有其他的读操作。

3、volatile多线程可见性的原理:(lock)写操作时锁住主内存;其他读操作必须在写操作完成后执行;嗅探机制使其它的栈缓存失效;

你可能感兴趣的:(volatile关键字与单例)