单例模式与双重锁

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

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

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会负责同步整个过程,在初始化进行一半的时候,别的线程无法使用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值