含义
确保某个类只有一个实例,且自实例化后向系统提供这个实例。避免产生多个对象消耗过多的资源。
UML类图
实现方式
懒汉方式
声明一个静态对象,且在用户第一次调用getInstance时进行初始化。
|
|
该方式单例在使用时才会实例化,synchronized
保证了多线程下的同步问题,但也导致了每次调用getInstanced
方法都要进行同步,造成不必要的开销。类加载快,获取单例慢。
饿汉方式
声明一个静态对象,并在类第一次加载的时候进行初始化。
|
|
该方式单例在类第一次加载的时候进行实例化,调用getInstanced
方法时不会有多线程问题。类加载慢,获取单例快。
双重检锁方式
在需要时初始化单例,调用获取单例方法不用进行同步锁。
|
|
volatile
作用:instance = new Singleton();
该语句并不是一个原子操作。它经过如下过程:
- 1.给instance实例分配内存;
- 2.调用构造函数,初始化成员字段;
- 3.将instance对象指向分配的内存空间(此时instance已经不是null了)。
加volatile
字段可以保证实例化时按1-2-3顺序执行;否则有可能是1-3-2顺序执行,此时该检锁方式将失效。
该方式资源利用率高,第一次执行getInstance
时单例才会被初始化。解决了资源消耗、多余同步、线程安全问题。但是,该方式在某些高并发的情况下出现失效问题,发生概率很小。
静态内部类单例方式
最推荐使用的单例实现方式。
|
|
该方式第一次加载Singleton
类时不会初始化instance,只有在第一次调用getInstance
的时候静态内部类会被加载并进一步完成instance的初始化。此方式还可以保证线程安全。
枚举方式
|
|
该方式写法简单,默认的枚举实例创建是线程安全的,且在任何情况下它都是一个单例。
总结
无论哪种方式,都是将构造函数私有化,并通过静态方法获取一个唯一实例,在这个过程中要保证线程的安全等。
此外还需要特别注意:如果我们的一个单例对象在内存中长久不使用,JVM就认为这个对象是一个垃圾,在CPU资源空闲的情况下该对象会被清理掉,下次再调用时就需要重新产生一个对象。所以在使用某些状态管理的单例时要特别注意。
鸣谢参考:《Android源码设计模式解析与实践》、《设计模式之禅》