赞
踩
单例模式是一种经典的设计模式,面试中非常常见
设计模式类似于“棋谱”,将编程中各种经典的问题场景进行整理,并且提供一些解决方案;设计模式其实有很多种,绝对不止23种,随着时代的变化,新的设计模式不断地诞生,旧的模式也就在消亡
简单来说,单例模式就是单个实例
代码中的有些对象,本身就不应该是有多个实例的,从业务角度就应该是单个实例
比如,写的服务器,要从硬盘上加载100G的数据到内存中(加载到若干个哈希表里)
肯定要写一个类,封装上述加载操作,并且写一些获取/处理数据的业务逻辑
这样的类,就应该是单例的,一个实例,就管理100G的内存数据,搞多个实例就是N*100G的内存数据,机器吃不消也没必要
再比如,服务器也可能涉及到一些“配置项”(MySQL有配置文件)
代码中也需要有专门的类,管理配置,需要加载配置数据到内存中供其他代码使用
这样的类的实例也应该是单例的,如果是多个实例,就存储了多份数据,如果一样还可以接受,如果不一样,以哪一个为准?
根本上保证对象是唯一实例,这样的代码,就称为单例模式,单例模式有很多不同的写法,下面主要介绍两种:饿汉模式与懒汉模式
package thread; class Singleton { private static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } private Singleton(){ } } public class Demo27 { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1==s2); } }
private static Singleton instance = new Singleton();
getInstance()
:此处后续需要使用这个类的实例,就可以通过getInstance()
来获取已经new好的这个实例,而不是重新实例化private Singleton(){ }
:
懒在计算机中往往是一个褒义词,而且是高效率的代表
懒汉模式不是在程序启动的时候创建实例,而是在第一次使用的时候才去创建
如果不使用了,就会把创建实例的代码节省下来了
如果代码中存在多个单例类
package thread; class SingletonLazy { private static SingletonLazy instance = null; public static SingletonLazy getInstance(){ if (instance == null){ instance = new SingletonLazy(); } return instance; } private SingletonLazy() { } } public class Demo28 { public static void main(String[] args) { SingletonLazy s1 = SingletonLazy.getInstance(); SingletonLazy s2 = SingletonLazy.getInstance(); System.out.println(s1 == s2); } }
if (instance == null) {instance = new SingletonLazy();}
:什么时候调用就什么时候创建,如果不调用,就不创建了
上述两种单例模式,是否是线程安全的
考虑有多个线程同时调用getInstance,是否会产生线程安全问题
对于饿汉模式
对于懒汉模式
if (instance == null) {instance = new SingletonLazy();}
:这行代码可以理解为,包含了读和写上述的懒汉模式造成的多线程不安全问题,本质是因为,如果执行顺序如下:
那么我们通过加锁的方式来进行尝试
public static SingletonLazy getInstance(){
if (instance == null){
synchronized (locker){
instance = new SingletonLazy();
}
}
return instance;
}
但是上述代码仍然存在问题:
于是应该把if和new打包成一个原子操作
public static SingletonLazy getInstance(){
synchronized (locker){
if (instance == null){
instance = new SingletonLazy();
}
}
return instance;
}
同时,上述代码还有修改的空间,懒汉模式只是在最开始调用getInstance会存在线程安全问题,一旦把实例创建好了,后续再调用,就只是读操作了,就不存在线程安全问题了,针对后续调用,明明没有线程安全问题,还要加锁,就是画蛇添足(加锁本身,也是有开销的,可能会使线程阻塞)
代码如下:
package thread; class SingletonLazy { private static volatile SingletonLazy instance = null; private static Object locker = new Object(); public static SingletonLazy getInstance(){ if (instance == null){ synchronized (locker){ if (instance == null){ instance = new SingletonLazy(); } } } return instance; } private SingletonLazy() { } } public class Demo28 { public static void main(String[] args) { SingletonLazy s1 = SingletonLazy.getInstance(); SingletonLazy s2 = SingletonLazy.getInstance(); System.out.println(s1 == s2); } }
volatile
也是有必要的,避免触发了优化,避免内存可见性与指令重排序问题Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。