面试官希望你回答单例能达到第二层
是什么 为什么
- 单例模式:是一种构建型模式,就是为了单个实例反复使用,达到减少开销的目的
单例模式的实现
1.私有的构造方法
私有的,保证不可以随便创建对象
2.私有的静态的当前类对象作为属性
static,只加载一次&&防止套娃
逻辑上,保证只new一次
公有不仅容易被创建也容易被访问
所以私有
3.公有的静态的方法返回当前类对象
因为当前类属性对象是私有的,所以我们要专门搞一个方法来获取对象
静态方法直接从类名就可以操作!!!
为什么要用静态方法呢?
因为由类加载顺序我们可以知道加载非静态的方法之前是要加载属性的,而属性有要求方法来获取创建,那不是死锁了吗?
(类成员的加载顺序:属性->代码块->方法,静态元素在类加载就已经初始化)
五种方式
- 饿汉式(立即加载) 对象启动时就加载啦
不会产生对象没有就拿来使用的问题 空指针异常
如果启动项目加载的对象过多 有些还没有使用 会产生服务器承载压力的问题(不过一般情况下你线上机器启动项目也不太可能这么弱)- 懒汉式(延迟加载) 对象什么时候用到了 才会加载
可能会由于没有操作好 导致异常(很严谨)(因为你不敢保证当时服务器还能承载多少的压力)
启动项目时候只有需要的加载 不需要的还没有创建 不会浪费空间
生命周期托管(单例对象别人帮我们处理) 对象加载过程交给别人
1、饿汉式
public class Singleton {
private static Singleton instance= new Singleton();
private Singleton() {
}
//直接返回已创建好的对象
public static Singleton getInstance() {
return instance;
}
}
优点:没加锁,更快
缺点:虽然 classloader 机制避免了多线程的同步问题,instance 在类装载时就实例化, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。有可能容易产生垃圾对象。
2、懒汉式
public class Singleton {
private static Singleton instance ;
private Singleton() {
}
//获取对象的时候再进行实例化
public static synchronized Singleton getInstance() {
if (instance == null) {
instance= new Singleton();
}
return instance ;
}
}
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,加锁变重,影响效率。
3、懒汉式-双重检验锁
public class Singleton {//懒汉式
//通过volatile关键字来确保安全 防止jvm的重排序
private volatile static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
获取时可能是半初始化对象,懒汉式通病了属于是,只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),因为Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。这意味着假如此时这个单例我们很消耗资源,它其实就不太适合了,因为你不敢保证当时服务器还能承载多少的压力,可以导致多少连锁反应。
4、饿汉式-静态内部类
public class Singleton {//饿汉式
//利用classloder的机制来保证初始化instance时只有一个线程
private static class SingletonHolder{
public static Singleton singleton = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.singleton;
}
}
简单又好用,利用了 classloader 机制来保证初始化 singleton 时只有一个线程
当然只适用于静态域的情况,而双检锁方式可在实例域需要延迟初始化时使用
这个也是线上常用的
5、枚举实现单例
最好的单例
它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
但是很少用它来实现单例
public enum Singleton {
INSTANCE
public void whatmethod(){
}
}