设计模式一:单例模式

含义

确保某个类只有一个实例,且自实例化后向系统提供这个实例。避免产生多个对象消耗过多的资源。

UML类图

实现方式

懒汉方式

声明一个静态对象,且在用户第一次调用getInstance时进行初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton{
private static Singleton instance;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}

该方式单例在使用时才会实例化,synchronized保证了多线程下的同步问题,但也导致了每次调用getInstanced方法都要进行同步,造成不必要的开销。类加载快,获取单例慢。

饿汉方式

声明一个静态对象,并在类第一次加载的时候进行初始化。

1
2
3
4
5
6
7
8
9
10
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}

该方式单例在类第一次加载的时候进行实例化,调用getInstanced方法时不会有多线程问题。类加载慢,获取单例快。

双重检锁方式

在需要时初始化单例,调用获取单例方法不用进行同步锁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton{
private static volatile Singleton instance = null;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){ //避免不必要的同步操作
synchronized(Singleton.class){ //null情况下创建实例
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}

volatile作用:instance = new Singleton();该语句并不是一个原子操作。它经过如下过程:

  • 1.给instance实例分配内存;
  • 2.调用构造函数,初始化成员字段;
  • 3.将instance对象指向分配的内存空间(此时instance已经不是null了)。

volatile字段可以保证实例化时按1-2-3顺序执行;否则有可能是1-3-2顺序执行,此时该检锁方式将失效。

该方式资源利用率高,第一次执行getInstance时单例才会被初始化。解决了资源消耗、多余同步、线程安全问题。但是,该方式在某些高并发的情况下出现失效问题,发生概率很小。

静态内部类单例方式

最推荐使用的单例实现方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton{
private Singleton(){
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
private static class SingletonHolder{
private static Singleton instance = new Singleton();
}
}

该方式第一次加载Singleton类时不会初始化instance,只有在第一次调用getInstance的时候静态内部类会被加载并进一步完成instance的初始化。此方式还可以保证线程安全。

枚举方式

1
2
3
4
5
6
7
public enum Singleton{
INSTANCE;
public void doSth(){
...
}
}

该方式写法简单,默认的枚举实例创建是线程安全的,且在任何情况下它都是一个单例。

总结

无论哪种方式,都是将构造函数私有化,并通过静态方法获取一个唯一实例,在这个过程中要保证线程的安全等。

此外还需要特别注意:如果我们的一个单例对象在内存中长久不使用,JVM就认为这个对象是一个垃圾,在CPU资源空闲的情况下该对象会被清理掉,下次再调用时就需要重新产生一个对象。所以在使用某些状态管理的单例时要特别注意。

鸣谢参考:《Android源码设计模式解析与实践》、《设计模式之禅》






0%