单例模式(从不好到完美)

单例模式的定义:确保某一个类只有一个实例,而且这个类可以自行实例化,并向整个系统提供这个实例。

最最简单的方法(饿汉模式):

public class Singleton {
    private static final Singleton singleton = new Singleton();
    // 通过将构造函数私有化,限制产生多个对象
    private Singleton() {
    }
    // 通过该方法获取实例对象
    public static Singleton getSingleton() {
        return singleton;
    }
    // 类中的其它方法尽量是static
    public static void doSomething() {
    }
}

最最简单的方法(懒汉模式):线程不安全

问题:

1. 如果一个线程A执行到singleton = new Singleton(),但是还没获取对象,第二个线程B也在执行,执行到(singleton == null)判断,那么线程B判断为真,于是继续执行,那么就会导致在内存中出现两个重复的对象。

2. 如果有N多个线程,每次执行到对象赋值操作的时候时间片就到了,那么就会有很多线程都判断singleton为null,所以产生好多对象实例,从而导致sinleton被多次赋值,最后return的时候也会返回不同的地址的实例对象。

解决办法:使用Synchronized对方法进行加锁

public class Singleton {
    private static Singleton singleton = null;
    // 限制产生多个对象
    private Singleton() {
    }
    // 通过该方法获取实例对象
    public static Singleton getSingleton() {
        if(singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

优化后的单例模式(对整个代码块进行加锁):

优点:简单粗暴,直接给把整个方法加锁

缺点:锁粒度太大,导致效率过低

public class Singleton {
    private static Singleton singleton = null;
    // 限制产生多个对象
    private Singleton() {
    }
    // 通过该方法获取实例对象
    public static synchronized Singleton getSingleton() {
        if(singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

再次优化,只对创建对象的部分进行加锁:

那么问题又来了,看波浪线那里,如果多个线程都跑到波浪线那里了,在每个线程拿到锁之后还是会进行多次赋值,所以还需要再优化,。

public class Singleton {
    private static Singleton singleton = null;
    // 限制产生多个对象
    private Singleton() {
    }
    // 通过该方法获取实例对象
    public static Singleton getSingleton() {
        if(singleton == null) {
 ---------------------------------------------
            synchronized(Singleton.class) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}

再优化:

改进点:再加一层判空。当一个线程拿到锁,再释放锁,那么对象必定已经实例化,其它线程再拿到锁的时候也必须再次判空,再判空的时候就发现变量已经被赋值,所以就退出返回

新问题:

再new一个对象的时候,会走以下过程,但是这三条指令也许会乱序执行,正常是123,乱序执行是132,因此如果线程A执行完13,时间片就到了,那么线程B进来发现变量已经不为空,所以就会返回一个没有进行第二步,即未进行初始化操作的对象,最后导致出错。

1.给SingletonLazy5的实例分配内存。

2.初始化SingletonLazy5的构造器

3.将singletonLazy对象指向分配的内存空间(注意到这步instance就非null了)。

public class Singleton {
    private static Singleton singleton = null;
    // 限制产生多个对象
    private Singleton() {
    }
    // 通过该方法获取实例对象
    public static Singleton getSingleton() {
        if(singleton == null) {
            synchronized(Singleton.class) {
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

终极方案:使用volatile防止重排序

public class Singleton {
    private static volatile Singleton singleton = null;
    // 限制产生多个对象
    private Singleton() {
    }
    // 通过该方法获取实例对象
    public static Singleton getSingleton() {
        if(singleton == null) {
            synchronized(Singleton.class) {
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值