java:单例模式

单例介绍

单例是指仅仅被实例化一次的类。
弊端:使客户端测试变的困难。

在这里插入图片描述

双重检查锁单例模式演变(Double check)

如下列举 饱汉模式(懒汉模式)下经过两次优化变为 double check。
并且解决synchronized为什么这么添加以及volatile问题。

1.单线程情况下:饱汉模式(懒汉模式)
public class Singleton {

    private static Singleton instance;
    //私有构造方法,禁止外部通过构造方法创建对象。
    private Singleton() {
        
    }
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
2.多线程下:

可以直接给方法加锁:

public class Singleton {

    private static Singleton instance;
    private Singleton() {

    }

    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
3.优化:

双重检查:

public class Singleton {

    private static Singleton instance;
    private Singleton() {

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

4.再优化:禁止指令重排

我们可以在代码块上加锁,而不是在方法上。

    synchronized (Singleton.class){ //3
        if (instance == null) { //4
            instance = new Singleton(); //5
        }
    }

在进入到代码块之前,只有在对象未实例情况下,才需要走上面的代码块。

        if(instance == null){ //2
            synchronized (Singleton.class){ //3
                if (instance == null) { //4
                    instance = new Singleton(); //5
                }
            }
        }

由此变成:


public class Singleton {

    private static  Singleton instance;
    private Singleton() {

    }
    public static Singleton getInstance() { //1
        if(instance == null){ //2
            synchronized (Singleton.class){ //3
                if (instance == null) { //4
                    instance = new Singleton(); //5
                }
            }
        }
        return instance;
    }
}

上面有两个问题:

  • 1.为什么synchronized代码块需要加在
  if (instance == null) { //4

外面?

  • 2.指令重排序问题。
4.1. 为什么synchronized代码块需要加在外面?

未加空的时候是这样的。
把4去掉。

public class Singleton {

    private static Singleton instance;

    private Singleton() {

    }

    public static Singleton getInstance() { //1
        if (instance == null) { //2
            synchronized (Singleton.class) { //3
                instance = new Singleton(); //5
            }
        }
        return instance;
    }
}

例如2个线程A,B。A,B都 走过了1-2。
A 进入3 创建了instance。
B 进入3 也创建了instance。

4.2.指令重排序问题。

编译器为了优化性能,有时候会改变程序中语句的先后顺序。

new操作会进行指令重排:
我们以为的new操作:

  1. 分配一块内存 M;
  2. 在内存 M 上初始化 Singleton 对象;
  3. 然后 M 的地址赋值给 instance 变量。

实际上优化后的执行路径:

  1. 分配一块内存 M;
  2. 将 M 的地址赋值给 instance 变量;
  3. 最后在内存 M 上初始化 Singleton 对象。

导致的空指针问题:
线程A 先执行 getInstance() 方法,执行指令2时,发生线程切换,切换为B。
B执行 getInstance() 方法,B执行 第一个instance == null时会发现不等于空。直接返回了instance。而此时instance是没有初始化过的,导致空指针。

在这里插入图片描述
添加volatile
保证程序的可见性,及时刷新cpu内存中的值。
禁止了指令重新排序。

volatile和synchronized区别在于只保证可见性,而不保证原子性。

public class Singleton {

    private static volatile Singleton instance;
    private Singleton() {

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

其他的单例模式:

1.枚举方式实现单例

该方式也是可以在多线程下面保证线程安全的,并且防止了序列化和反射。《Effect Java》中也推荐使用此方式来实现单例。
并不是使用枚举就不需要保证线程安全,只不过线程安全的保证不需要我们关心。

public enum  Singleton {
    INSTANCE;

    public void doSomething(){
        System.out.println("do sth.");
    }
}

为什么我墙裂建议大家使用枚举来实现单例。

2.饿汉式

顾名思义,在成员变量的时候就会初始化。
缺点:
类加载时就初始化,浪费内存,容易产生垃圾对象。

public class Singleton {
    private static final Singleton instance = new Singleton();
    private Singleton() {
    }
    public static Singleton getInstance() { //1
        return instance;
    }
}

既然类加载时候初始化,我们可以放在代码块中:

3.饿汉式(变种)
public class Singleton {
    private static  Singleton instance = null;
    static {
        instance = new Singleton();
    }
    private Singleton() {
    }
    public static Singleton getInstance() { //1
        return instance;
    }
}

那么我们放到静态内部类中也是可以的,也就是holder模式。

4.静态内部类模式(holder模式)
public class Singleton {

    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }

    private Singleton() {
    }

    public static Singleton getInstance() { //1
        return SingletonHolder.instance;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值