0
点赞
收藏
分享

微信扫一扫

设计模式 单例模式

倪雅各 2022-03-11 阅读 67
java

解释

确保一个类只有一个实例,并且自己实例化唯一的一个对象

场景

当一个对象没有成员变量时候(无状态对象),可以通过单例模式创建
当一个对象需要产生较多资源开销的时候,可以用单例模式创建一个对象,让他永久驻留内存

最简单的单例模式,可能存在并发问题

public class Singleton {
    private static Singleton singleton = null;
    //构造函数设置为私有的,限制通过构造函数创建对象
    private Singleton() {
    }
    public static Singleton getInstance(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

我们来分析一下再多线程情况下可能存在的问题

  //线程A
    public static Singleton getInstance(){
    1    if(singleton==null){ //cup 1
    2        singleton = new Singleton();// cup 3
    3    }
    4    return singleton;
    }
    //线程B
    public static Singleton getInstance(){
    1    if(singleton==null){// cup 2
    2        singleton = new Singleton();//cup 4
    3    }
    4    return singleton;
    }

cup 1、2…是指线程获得cup时间片,两段代码是属于同一个方法
当线程A执行到行数1时候,表达式返回结果为true
这个时候线程B获得到cup资源,也执行到行数1,表达式返回结果也为true
然后线程A和线程B会分别创建两个对象,违背了单例的初衷

单例模式经典双重校验

public class Singleton {
    private static volatile Singleton singleton = null;
    private Singleton() {
    }

    public static Singleton getInstance(){

     1   if(singleton==null){
     2       synchronized(Singleton.class) {
     3           if(singleton==null) {
     4               singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

synchronized为何不放在方法上
如果 使用 synchronized Singleton getInstance(),那么每个线程调用getInstance时候都要争抢锁资源

为什么要加两层if判断
1.当线程A判断第一个if(行1)时候返回true就会去争抢锁资源,这个时候抢到锁资源,但是还没执行4。
2.这个时候线程B去读取第一个if(行1) 是,因为线程A的行4还没执行,所以也返回true。
3.线程A执行完行4,并且释放锁资源,线程B获取锁资源,判断第二if(行3),这个时候因为线程A已经把对象Singleton赋值给引用singleton,第二个if(行3)返回false,就不会在进入行4重复创建对象

为什么要加volatile限定词
因为 singleton = new Singleton();不是一个原子操作。分为三个操作指令
1.给Singleton分配内存。
2.初始化Singleton对象。
3.把对象Singleton赋值给引用singleton。
java执行程序会对指令进行重排序,线程A存在先执行3再进行2的情况,那么就有可能导致未完全初始化的对象(只执行步骤3)赋值给引用singleton,当线程B在第一个if(行1)判断时返回true,而此时拿到的对象是个半成品
volatile的作用是,Java线程内存模型确保所有线程看到这个变量的值是一致的,同时还会禁止指令重排序

举报

相关推荐

0 条评论