单例模式算是我们最常见的设计模式了,不仅仅在项目中常用,在找工作的笔试中也是高频发的试题了,那么我们现在来认识一下单例模式。
单例模式很难吗?
- 单例模式其实严格意义来说有八种写法,在面试中常常问到的当然是有关于内存与同步问题的单例模式啦,所以好好认识下单例模式还是有必要的。
单例模式的几种推荐写法与讲解。
最简单的单例实现
public class Singleton1 {
// 类装载的时候即已经实例化
private final static Singleton1 instance = new Singleton1();
// 构造器私有化
private Singleton1() {}
// 提供公有的获取方法
public static Singleton1 getInstance() {
return instance;
}
}
这种实现方式虽然说不是最好的实现方式,但是是最常用的单例的实现方式。因为是类一开始即被装载,所以不用担心线程安全的问题。但是缺点就是如果不使用这个类,就会造成内存浪费的问题。
双重检查模式
public class Singleton6 {
// volatile声明作用即是内存变量共享的作用
private static volatile Singleton6 instance;
// 构造器私有化
private Singleton6() {}
// 提供公有的获取方法
public static Singleton6 getInstance() {
if (instance == null) {
synchronized (Singleton6.class) {
if (instance == null) {
instance = new Singleton6();
}
}
}
return instance;
}
}
双重检查的写法其实是通过声明了volatile和双重加锁的方式实现了单例模式,有的人可能会问,为什么锁的内部还要再加一层判断,其实在考虑多线程同时进入了第一层if判断中时,都在等待着锁的释放,但是释放之后其他线程已经进入了第一层,那么单例模式的结构就会被打乱了,其实锁也可以加在方法上,只不过锁的粒度问题,节省了一点内存。
静态内部类实现(敲黑板)
public class Singleton7 {
// 构造器私有化
private Singleton7() {}
// 静态内部类不会在一开始被装载 所有没有内存消耗问题
// JVM在装载静态内部类是线程安全的 只有在使用内部类才会去装载 所以线程是安全的
private static class SingletonInstance {
private static final Singleton7 singleton7 = new Singleton7();
}
// 提供静态公有获取方法
public static synchronized Singleton7 getInstance() {
return SingletonInstance.singleton7;
}
}
这种静态内部类实现的单例模式是笔试中面试官最想看到的答案,因为其中涉及到写JVM装载的问题。 有必要提一下JVM装载内部类并不是程序启动就装载,而且装载内部类是线程安全的,所以这个单例模式真正意义上实现了懒加载与线程安全且节省了内存,需要掌握的一种实现方式。
冷门的枚举实现方式
enum Singleton {
INSTANCE;
public void updateInstance () {}
}
优点防止多线程同步问题,而且还能防止反序列化重新创建新的对象,这种实现方式不是很常见了解即可。
补充
- spring中单例的运用常见如下:
<bean id="" class="" scope="singleton"/>
- 也可以通过声明的方式 :@Scope("singleton")
源码下载地址:github.com/Liyinzuo/De…
如果有错误的地方,欢迎指正。