定义:确保一个类中只有一个实例,而且自行实例化并向整个系统提供这个实例。或者说某种类型的对象有且只有一个。
实现单例模式主要有以下几个关键点:
- 构造函数不对外开放,一般为Private
- 通过一个静态方法或者枚举返回单例类对象
- 确保单例类的对象有且只有一个,尤其是在多线程环境下
- 确保单例类对象在反序列化时不会被重新构建对象。
单例的实现方式有多重:饿汉式,懒汉式,静态内部类,双重校验锁(DCL),枚举等,推荐使用静态内部类和双重校验锁。
1.饿汉式:
/**
* Created by jmfstart on 2017/5/5.
* 饿汉式
*/
public class Singleton {
private static final Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
这种方式基于类加载机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到懒加载的效果。
2.懒汉式(线程不安全的)
/**
* Created by jmfstart on 2017/5/5.
* 懒汉式
*/
public class Singleton {
private static Singleton singleton ;
private Singleton() {
}
public static Singleton getInstance() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
这种写法懒加载很明显,但是致命的是在多线程不安全。
3.懒汉式(线程安全的)
public class Singleton {
private static Singleton instance;
private Singleton (){
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
大家细想,假如对象已经被初始化instance,现在每次调用getInstance()方法时都会进行同步,这会消耗不必要的资源。不建议使用
4.Double Check Lock(DCL)实现单例
/**
* Created by jmfstart on 2017/5/5.
* DCL方式实现
*/
public class Singleton {
private static Singleton singleton ;
private Singleton() {
}
public static Singleton getInstance() {
if(singleton == null) {
synchronized (Singleton.class){
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
优点:资源利用率高。
缺点:第一次加载时反应稍慢。在高并发环境下也有一定的缺陷,虽然发生的概率很小。
DCL模式是使用最多的单例实现方式,它能够在需要时才实例化单例对象。这种方式一般能够满足需求。
5.静态内部类单例模式
DCL在某些情况下出现失效的问题,这个问题被称为双重检验锁(DCL)失效,建议使用静态内部类替代
/**
* Created by jmfstart on 2017/5/5.
* 静态内部类实现单例
*/
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.singleton;
}
//静态内部类
private static class SingletonHolder {
private static Singleton singleton = new Singleton();
}
}
这种方式不仅能够保证线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化,所以这是推荐使用的单例模式的实现方式。
6.枚举类实现单例
/**
* Created by jmfstart on 2017/5/5.
* 枚举实现单例
*/
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("do sth.");
}
}
这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。
7.使用容器实现单例模式
这种实现方式比较另类
/**
* Created by jmfstart on 2017/5/5.
* 单例的集合,便于管理
*/
public class SingletonManager {
private static Map<String,Object> objectMap = new HashMap<>();
private SingletonManager(){
}
private static void registerService(String key,Object instance){
if(objectMap.containsKey(key)) {
objectMap.put(key,instance);
}
}
public static Object getService(String key){
return objectMap.get(key);
}
}
单例模式总结
优点
a.单例模式在内存中只有一个实例,减少内存开支,非常适合频繁的创建和销毁一个对象的场景
b.由于单例模式只生成一个实例,所以减少了系统的性能开支,使用永久驻留内存的方式来解决。
c.单例模式避免了对公共资源的多重占用。如读写文件操作等等。
d.单例模式可以在系统设置全局的访问点。
缺点
a.单例模式一般没有接口,扩展很困难,除了修改代码基本没有其他方法可以实现。
b.单例对象如果持有Context,那么很容易引起内存泄露,最好传递Application Context.