大家好,我是程序员阿药。今天和大家分享的是面试中设计模式部分常问的一种:单例模式。
我了解到的单例模式一共八种,两种饿汉式、三种懒汉式、双重检查、静态内部类、枚举。双重检查方式一般问到的比较多,相对也比较复杂,下面也会详细介绍。
介绍之前先说点别的。
一、为什么要使用单例模式,好处?
单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
二、怎么使用单例模式,方法,注意事项?
当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new。单例类的构造方法一定要私有化。
三、单例模式使用的场景?
需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)。
下面开始详细介绍各种单例模式。
1. 饿汉式(静态变量实现)
class Singleton {
/**
* 1.构造器私有化,外部不能new
*/
private Singleton() {
}
/**
* 2.本类内部创建对象实例
*/
private final static Singleton INSTANCE = new Singleton();
/**
* 3.提供一个共有的静态方法,返回实例对象
*/
public static Singleton getInstance() {
return INSTANCE;
}
}
2. 饿汉式(静态代码块实现)
class Singleton {
/**
* 1.构造器私有化,外部不能new
*/
private Singleton() {
}
/**
* 2.本类内部创建对象实例
*/
private static final Singleton INSTANCE;
static {
INSTANCE = new Singleton();
}
/**
* 3.提供一个共有的静态方法,返回实例对象
*/
public static Singleton getInstance() {
return INSTANCE;
}
}
饿汉式是立即加载的方式,无论是否会用到这个对象,都会加载。如果在构造方法里写了性能消耗较大,占时较久的代码,比如建立与数据库的连接,那么就会在启动的时候感觉稍微有些卡顿。
3. 懒汉式(静态方法实现)
class Singleton {
private static Singleton instance;
private Singleton() {
}
/**
* 提供一个静态共有方法,当使用到的时候,采取创建instance
* 实际开发不推荐,线程不安全
*/
public static Singleton getInstance() {
// (此处线程不安全,多个线程有可能创建多个对象)
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
4. 懒汉式(同步方法实现)
class Singleton {
private static Singleton instance;
private Singleton() {
}
/**
* 提供一个静态共有方法,当使用到的时候,采取创建instance
* 添加同步方法,解决线程安全问题
* 实际开发不推荐,效率低
*/
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
5. 懒汉式(同步代码块实现)
class Singleton {
private static Singleton instance;
private Singleton() {
}
/**
* 提供一个静态共有方法,当使用到的时候,采取创建instance
* 添加同步代码块,解决线程安全问题
* 实际开发不推荐,效率低
*/
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
懒汉式是延迟加载的方式,只有使用的时候才会加载。并且有线程安全的考量。使用懒汉式,在启动的时候,会感觉到比饿汉式略快,因为并没有做对象的实例化。但是在第一次调用的时候,会进行实例化操作,感觉上就略慢。
6. 双重检查(重点)
class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
/**
* 提供一个静态共有方法,当使用到的时候,采取创建instance
* 添加双重检查代码,解决线程安全问题,同时解决懒加载问题,同时保证了效率
* 实际开发推荐
*/
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查模式中当有一个线程创建对象成功后,由于instance对象使用volatile修饰保证了对象的可见性,其它线程会立即知道对象成功创建成功,待竞争锁成功的线程再进入同步代码块后将不会二次创建对象;同时,以后到来的线程也不再需要进入同步代码块判断对象创建成功,通过同步代码块外部的条件判断即可立即返回。
注意:
volatile可以保证变量的可见性和有序性,不可以保证原子性。instance对象使用volatile修饰除了通过可见性保证其它线程立即可见外,在创建单例对象时也通过其有序性保证了对象的成功创建。
对象创建分为1.申请空间、2.初始化对象、3.指向申请的空间三步。如果在创建过程中不保证其有序性,创建顺序变成1、3、2的话,那么如果有新的线程到来,在执行同步代码块外的条件判断时发现对象不为null立即返回;但此时对象并未完成初始化,最终将导致对象无法使用。
7. 静态内部类
class Singleton {
/**
* 构造器私有化
*/
private Singleton() {
}
/**
* 写一个静态内部类,该类中有一个静态属性Singleton
*/
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
/**
* 提供一个静态共有方法,直接返回SingletonInstance.INSTANCE
*/
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
8. 枚举
enum Singleton {
INSTANCE;
}
以上内容如果有写的不好的地方欢迎大家批评指正。
今天想分享的就是这些,我是程序员阿药,喜欢的小伙伴可以点赞、收藏支持一下哦。
更多文章可以关注我的微信公众号:“程序员阿药”。