美文网首页
单例模式与双重锁

单例模式与双重锁

作者: 乐百事52淑熙 | 来源:发表于2018-05-11 13:31 被阅读0次

设计模式中,最为基础与常见的就是单例模式。这也是经常在面试过程中被要求手写的设计模式。

下面就先写一个简单的单例:

public class Singleton {

    private static Singleton singleton =new Singleton();

    private void Singleton(){}

    public static Singleton getSingleton(){

        return singleton;

    }

}

上面是饿汉式单例:jvm在启动的时候直接在内存中初始化一个单例对象,我们在调用getSingleton()时直接获取该对象。

public class Singleton {

private static Singleton singleton =null;

    private void Singleton(){}

    public static Singleton getSingleton(){

        if(singleton==null){

            singleton = new Singleton();

        }

        return singleton;

    }

}

上面的是懒汉机制:在jvm启动的时候不会初始化单例对象,只有调用getSingleton()时才去创建对象。多线程的情况下会出现不同步问题,因此需要加锁。

public class Singleton {

private static Singleton singleton =null;

    private void Singleton(){}

    public static synchronized Singleton getSingleton(){

    if(singleton==null){

        singleton  = new Singleton();

        }

    return singleton;

    }

}

懒汉加锁之后,我们调用getSingleton()方法时,会在该方法上面加锁,每次只允许一个线程进入,可以解决同步问题,但是性能会下降。

public class Singleton {

    private static Singleton singleton =null;

    private void Singleton(){}

    public static Singleton getSingleton(){

    if(singleton==null){                                   @1

        synchronized (Singleton.class){          @2

            if(singleton==null){                            @3

                singleton = new Singleton();          @4

                }

        }

    }

        return singleton;

    }

}

在懒汉加锁基础上编程双锁机制:解决了同步时的性能问题。但是在多线程的情况下,还是会出现问题,问题出现在哪里?

 singleton = new Singleton();          @4 

这一步实例化的过程有问题:

这一行代码可以分解为如下的三行伪代码:

  memory = allocate();   //1:分配对象的内存空间

  ctorInstance(memory);  //2:初始化对象

  instance = memory;     //3:设置instance指向刚分配的内存地址

在JIT编译器中,可能会发生重排序。在重排的情况下:如果a,b两个线程同时调用getSingleton()方法,例如a线程先获取到,在a线程 singleton = new Singleton(); 时发生重排,执行1,3,2,执行到3,b线程获取singleton的没有被初始化。

如何解决:

第一:加关键字volatile

public class Singleton {

    private static volatile Singleton singleton =null;

    private void Singleton(){}

    public static Singleton getSingleton(){

        if(singleton==null){

            synchronized (Singleton.class){

                if(singleton==null){

                    singleton =new Singleton();

                    }

            }

        }

    return singleton;

    }

}

Volatile关键字: 可以解决可见性问题,不能确保原子性问题(通过 synchronized 进行解决), 禁止指令的重排序(单例主要用到此JVM规范)。

第二:利用静态内部类

public class Singleton {

    private static Singleton singleton =null;

    private void Singleton(){}

    private static class StaticSingleton{

        private static final Singleton SINGLETON =new Singleton();

    }

    public static Singleton getSingleton(){

        if(singleton==null){

            synchronized (Singleton.class){

                if(singleton==null){

                    singleton = StaticSingleton.SINGLETON;

                }

        }

    }

    return singleton;

    }

}

静态内部类确保在第一次初始化的时候,不用担心并发问题。因为jvm会负责同步整个过程,在初始化进行一半的时候,别的线程无法使用。

个人公号:【排骨肉段】,可以关注一下。

相关文章

网友评论

      本文标题:单例模式与双重锁

      本文链接:https://www.haomeiwen.com/subject/myxxdftx.html