上一篇推文写了工厂方法模式,包括简单工厂模式、多工厂模式、静态工厂模式、抽象工厂模式,这篇推文记录一下单例设计模式
在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。省去了new操作符,降低了系统内存的使用频率,减轻GC压力。有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
先创建实例,用静态方法调用优点:实现起来简单,没有多线程同步问题。
缺点:当类Singleton被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间,从这以后,这个static的instance对象便一直占着这段内存(即便你还没有用到这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存。
public class SingleTon {
//在类的内部创建一个类的实例
private static SingleTon instance = new SingleTon();
//私有构造方法,防止被实例化
private SingleTon() {
}
//如果该对象被用于序列化,可以保证对象在序列化前后保持一致
public Object readResolve() {
return instance;
}
//私有化此对象,通过公共的方法来调用,此公共方法只能用类来条用(static修饰),同时类的实例也应为static
public static SingleTon getInstance(){
return instance;
}
}
class Test{
public static void main (String[] args){
SingleTon s1 = SingleTon.getInstance();
SingleTon s2 = SingleTon.getInstance();
System.out.println(s1 == s2); //true
}
}
在静态方法创建实例并直接调用如果不是线程同步,在多线程环境下,可能线程1正在创建单例时,线程2判断单例instance为空,就会创建多个实例优点:实现起来比较简单,当类Singleton被加载的时候,静态变量static的instance未被创建并分配内存空间,当getInstance方法第一次被调用时,初始化instance变量,并分配内存,因此在某些特定条件下会节约了内存。
缺点:在多线程环境中,这种实现方法是完全错误的,根本不能保证单例的状态。
public class SingleTon {
//在类的内部创建一个类的实例
private static SingleTon instance = null;
private SingleTon(){}
public static SingleTon getInstance(){
if(instance == null){
instance = new SingleTon();
}
return instance;
}
}
class Test{
public static void main (String[] args){
SingleTon s1 = SingleTon.getInstance();
SingleTon s2 = SingleTon.getInstance();
System.out.println(s1 == s2); //true
}
}
优点:在多线程情形下,保证了“懒汉模式”的线程安全。
缺点:众所周知在多线程情形下,synchronized 方法通常效率低,显然这不是最佳的实现方案。例如SingleTon实例不为null的情况下,线程同步机制必须等待才能进入if块,而如果存在实例,完全可以跳过等待,直接返回,因为不用进入if块。
public class SingleTon{
private SingleTon(){}
private static SingleTon instance = null;
public static synchronized SingleTon getInstance(){
if(instance == null)
instance = new SingleTon();
return instance;
}
}
public class SingleTon{
private SingleTon(){}
private static SingleTon instance = null;
public static SingleTon getInstance(){
// 第一次检查instance是否被实例化出来,如果没有则进入if块
if(instance == null){
synchronized (SingleTon.class) {
// 某个线程取得了类锁,实例化对象前第二次检查instance是否已经被实例化出来,如果没有,才最终实例出对象
if (instance == null) {
instance = new SingleTon();
}
}
}
return instance;
}
}
单例模式理解起来简单,但是具体实现起来还是有一定的难度。synchronized关键字锁定的是对象,在用的时候,一定要在恰当的地方使用(注意需要使用锁的对象和过程,可能有的时候并不是整个对象及整个过程都需要锁)