什么是单例模式
面试官问什么是单例模式时,千万不要答非所问,给出单例模式有两种类型之类的回答,要围绕单例模式的定义去展开。
单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。
单例模式的类型
单例模式有两种类型:
懒汉式
:在真正需要使用对象时才去创建该单例类对象饿汉式
:在类加载时已经创建好该单例对象,等待被程序使用
懒汉式创建单例对象
懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已实例化直接返回该类对象。否则则先执行实例化操作。
根据上面的流程图,就可以写出下面的这段代码
public class Singleton { private static Singleton singleton; private Singleton(){} public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } }
没错,这里我们已经写出了一个很不错的单例模式,不过它不是完美的,但是这并不影响我们使用这个“单例对象”。
以上就是懒汉式创建单例对象的方法,我会在后面解释这段代码在哪里可以优化,存在什么问题。
饿汉式创建单例对象
饿汉式在类加载
时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。
关于类加载,涉及到JVM的内容,我们目前可以简单认为在程序启动时,这个单例对象就已经创建好了。
public class Singleton{ private static final Singleton singleton = new Singleton(); private Singleton(){} public static Singleton getInstance() { return singleton; } }
注意上面的代码在第3行已经实例化好了一个Singleton对象在内存中,不会有多个Singleton对象实例存在
类在加载时会在堆内存中创建一个Singleton对象,当类被卸载时,Singleton对象也随之消亡了。
懒汉式如何保证只创建一个对象
我们再来回顾懒汉式的核心方法
public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; }
这个方法其实是存在问题的,试想一下,如果两个线程同时判断 singleton 为空,那么它们都会去实例化一个Singleton 对象,这就变成多例了。所以,我们要解决的是线程安全
问题。
最容易想到的解决方法就是在方法上加锁,或者是对类对象加锁,程序就会变成下面这个样子
public static synchronized Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; } // 或者 public static Singleton getInstance() { synchronized(Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } return singleton; }