设计模式之单例模式

45 阅读3分钟

首先什么是单例模式?为什么需要单例模式? 答:单例模式顾名思义就是在整个运行时,一个类只有一个实例对象

因为有的类的实例对象创建和销毁对资源来说消耗不大,但是有些类比较庞大和复杂如果频繁的创建和销毁对象,并且这些对象完全是可以复用的,会造成不必要的性能浪费

单例模式有多种写法需要考虑:

是否线程安全 是否是懒加载 能否能被反射破坏(这个不用考虑一般都是会被破坏的) 不会满足以上三点的单例

如下:首先来一个简单的单例

public class Singleton { private Singleton(){} //构造器私有 private static Singleton instance = null; //初始化对象为null public static Singleton getInstance(){ if (instance == null){ instance = new Singleton(); } return instance; } }

上面代码如果其他类要使用singleton对象的话只能通过调用getInstance方法,进入方法后首先判断是否被构造过,如果是就直接使用,如果不是就new一个新的出来再使用。

如果要保证在同一时刻只有一个线程进入把代码改成如下代码:

public class Singleton { private Singleton(){} private static Singleton instance = null; public static synchronized Singleton getInstance(){ if (instance == null){ instance = new Singleton(); } return instance; } }

但是以上写法不是懒加载的。

我们现在看第二次写的代码,为何性能影响大,原因就在于在方法上定义了synchronized关键字,所以每次进入该方法的时候就必须首先去获取锁。

所以我们把代码改成如下:

public class Singleton { private static Singleton singleton; private Singleton(){} public static Singleton getSingleton(){ if (singleton == null){ synchronized (Singleton.class){ if (singleton == null){ singleton = new Singleton(); } } } return singleton; } }

以上代码运用了双检锁,双检锁是一种很常见的同步代码手段。

下面使用volatile就会阻止指令重排问题 public class Singleton { private volatile static Singleton singleton; private Singleton(){} public static Singleton getSingleton(){ if (singleton == null){ synchronized (Singleton.class){ if (singleton == null){ singleton = new Singleton(); } } } return singleton; } }

上面这种写法既满足了懒加载,又满足了线程安全问题,效率也比较高。

那还有没有更简洁的写法,如下:内部类的写法

`public class Singleton { private static class SingletonHolder{ private static final Singleton INSTANCE = new Singleton();

} private Singleton(){} public static final Singleton getInstance(){ return SingletonHolder.INSTANCE; } }`

这里,静态内部类在程序启动的过程中不会加载,只有在第一次被调用的时候才会被加载。这里主要是运用了jdk加载特性实现了懒加载。

但是上面我们说了会被反射破坏,但是可以利用枚举类型来不让反射破坏,但是枚举类型无法实现懒加载模式。

第二种饿汉式如下:

`public class Singleton { //饿汉式如下: private static Singleton instance = new Singleton();

private Singleton(){}
public static Singleton getInstance(){
    return instance;
}

}`

  • 优点 :
    1.线程安全
    2.在类加载的同时已经创建好一个静态对象调用时反应速度快
  • 缺点 :
    资源效率不高,可能getInstance()永远不会执行到,但执行该类的其他静态方法或者加载了该类(class.forName) ,那么这个实例仍然初始化