[设计模式] 创建型:单例模式(Singleton Pattern)

什么是单例模式

单例就是单个对象的意思,指在系统运行期间,一个类最多只能创建一个对象,且该类能自行创建这个对象的一种编码设计模式。

单例模式有两个特点:

  1. 在系统的整个生命周期内,单例对象最多只能有一个
  2. 单例对象必须由单例类自行创建,并对外提供访问入口

一个类可以创建多个对象,这是面向对象的语言特性,想要实现单例模式,就要屏蔽这个特性,防止系统可以随意创建类的对象。

要做到这一点,通常做法就是利用private关键字将类的构造方法私有化,使外部调用者无法利用new关键字创建类的对象。一旦私有化了类的构造方法,就意味着能够使用new关键字创建对象的权利只有该类自己拥有!所以该类就必须自己创建单例,并对外界提供可访问该单例对象的静态方法。

为什么是静态方法?因为外界不可能创建单例类的对象,也就没有可能直接调用对象的方法。再进一步可推测出单例对象也要用static关键字修饰,因为静态方法中无法访问对象成员变量。

单例模式的实现

饿汉模式

单例类加载或者系统启动的时候,就创建好单例对象,不管后面会不会使用。

public class Singleton {
	private static Singleton singleton = new Singleton();

	private Singleton() {
	}

	public static Singleton getInstance() {
		return singleton;
	}
}

饿汉模式不存在线程安全问题,但是也要注意在类加载、系统启动的时候会不会有其它问题。

懒汉模式

系统启动运行期间不创建单例对象,只在第一次调用单例类入口方法时才去创建单例对象。

特点:不使用不创建,使用时才创建,尽可能延迟单例对象的实例化。

具体代码实现又可分为两大应用场景:线程不安全、线程安全。

线程不安全

public class Singleton {
    private static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

线程安全

保证线程安全的第一种方式:简单粗暴,直接使用synchronized静态方法。

public class Singleton {
	private static Singleton singleton;

	private Singleton() {
	}

	public static synchronized Singleton getInstance() {
		if (singleton == null) {
			singleton = new Singleton();
		}
		return singleton;
	}
}

保证线程安全的第二种方式:双重加锁检查DCL(Double Check Lock)

public class Singleton {
	// volatile关键字必须有
	private volatile static Singleton singleton;

	private Singleton() {
	}

	public static Singleton getInstance() {
		if (singleton == null) {// 第一次校验
			synchronized (Singleton.class) {// 加锁
				if (singleton == null) {// 第二次校验
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}
}

双重加锁检查的要点有两个:

  1. 单例对象需要使用volatile修饰,保证单例对象完全初始化后才能被访问到
  2. 加锁前后,两次检查单例对象是否已被创建,防止多次重复创建

保证线程安全的第三种方式:静态内部类

public class Singleton {
	private static class Holder {
		private static Singleton singleton = new Singleton();
	}

	private Singleton() {
	}

	public static Singleton getInstance() {
		return Holder.singleton;
	}
}

这种方式特点就是不需要加锁,利用内部类延时加载机制达到单例类的延时创建效果,值得推荐和使用!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章