1 Singleton 单例模式
目的:确保对象的唯一性;
实现:这种模式涉及一个类,它负责创建一个对象,同时确保只创建一个对象。这个类提供了一种方法来访问它的唯一对象,可以直接访问,而不需要实例化类的对象。
2 主要实现
2.1 饿汉模式
饿汉模式没有加锁,执行效率会提高,它基于 classloder 机制避免了多线程的同步问题,在单例模式中大多数都是调用静态方法来获取实例。
2.1.1 饿汉模式
public class Singleton {
// 饿汉模式 创建 Singleton 的一个对象
private static Singleton singleton = new Singleton();
// 让构造函数为 private,这样该类就不会被实例化
private Singleton() {
}
// 获取唯一可用的对象
public static Singleton getInstance() {
return singleton;
}
public void showMessage() {
System.out.println("Hello World!");
}
}
2.2 懒汉模式
懒汉模式因为设计到线程安全所以有多种实现。
2.2.1 懒汉模式 无锁
这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。
因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
public class SingletonLazy {
private static SingletonLazy singletonLazy;
private SingletonLazy() {
}
public static SingletonLazy getInstance() {
singletonLazy = new SingletonLazy();
return singletonLazy;
}
public void showMessage() {
System.out.println("Hello World! I`m Miss Lazy~");
}
}
2.2.2 懒汉模式 加锁
种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。
public class SingletonLazySynchronized {
private static SingletonLazySynchronized singletonLazy;
private SingletonLazySynchronized() {
}
public static synchronized SingletonLazySynchronized getInstance() {
if (singletonLazy == null) {
singletonLazy = new SingletonLazySynchronized();
}
return singletonLazy;
}
public void showMessage() {
System.out.println("Hello World! I`m Miss Lazy Synchronized~");
}
}
2.2.3 懒汉模式 重入锁
实现思想跟同步锁一样:
public class SingletonLazyReentrantLock {
private static SingletonLazyReentrantLock singletonLazyReentrantLock;
private static ReentrantLock lock = new ReentrantLock();;
private SingletonLazyReentrantLock() {
}
public static SingletonLazyReentrantLock getInstance() {
lock.lock();
if (null == singletonLazyReentrantLock) {
singletonLazyReentrantLock = new SingletonLazyReentrantLock();
}
lock.unlock();
return singletonLazyReentrantLock;
}
public void showMessage() {
System.out.println("Hello World! I`m Miss Lazy ReentrantLock~");
}
}
2.2.4 懒汉模式 双锁
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。
public class SingletonLazyDoubleLock {
private volatile static SingletonLazyDoubleLock singleton;
private SingletonLazyDoubleLock() {
}
public static synchronized SingletonLazyDoubleLock getInstance() {
if (singleton == null) {
synchronized (SingletonLazyDoubleLock.class) {
if (singleton == null) {
singleton = new SingletonLazyDoubleLock();
}
}
}
return singleton;
}
public void showMessage() {
System.out.println("Hello World! I`m Miss Lazy DoubleLock~");
}
}
2.2.5 懒汉模式 内部类
这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloder 机制来保证初始化 instance 时只有一个线程,它跟饿汉模式不同的是:饿汉模式只要 Singleton 类被装载了,那么 singleton 就会被实例化(没有达到 lazy loading 效果),而这种方式是 SingletonLazyInnerClass 类被装载了,singleton不一定被初始化。因为 SingletonLazyInnerClass 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonLazyInnerClass 类,从而实例化 singleton。
想象一下,如果实例化 singleton很消耗资源,所以想让它延迟加载,另外一方面,又不希望在单例类加载时就实例化,因为不能确保单例类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化显然是不合适的。这个时候,这种方式相比单例就显得很合理。
public class SingletonLazyInnerClass {
private static class SingletonHolder {
private static final SingletonLazyInnerClass INSTANCE = new SingletonLazyInnerClass();
}
private SingletonLazyInnerClass() {
}
public static final SingletonLazyInnerClass getInstance() {
return SingletonHolder.INSTANCE;
}
public void showMessage() {
System.out.println("Hello World! I`m Miss Lazy InnerClass~");
}
}
2.3 调用
public class Client {
public static void main(String[] args) {
// 调用构造器会报错
// Singleton s = new Singleton();
// 调用其静态方法获取实例
Singleton instance = Singleton.getInstance();
instance.showMessage();
// 调用其静态方法获取实例 懒加载 无锁
SingletonLazy instanceLazy = SingletonLazy.getInstance();
instanceLazy.showMessage();
// 调用其静态方法获取实例 懒加载 加锁
SingletonLazySynchronized instanceLazySynchronized = SingletonLazySynchronized.getInstance();
instanceLazySynchronized.showMessage();
// 调用其静态方法获取实例 懒加载 重入锁
SingletonLazyReentrantLock instanceLazyReentrant = SingletonLazyReentrantLock.getInstance();
instanceLazyReentrant.showMessage();
// 调用其静态方法获取实例 懒加载 双锁机制
SingletonLazyDoubleLock instanceLazyDcl = SingletonLazyDoubleLock.getInstance();
instanceLazyDcl.showMessage();
// 调用其静态方法获取实例 懒加载 内部类 类加载时单线程机制
SingletonLazyInnerClass instanceLazyInner = SingletonLazyInnerClass.getInstance();
instanceLazyInner.showMessage();
}
}
结果:
Hello World!
Hello World! I`m Miss Lazy~
Hello World! I`m Miss Lazy Synchronized~
Hello World! I`m Miss Lazy ReentrantLock~
Hello World! I`m Miss Lazy DoubleLock~
Hello World! I`m Miss Lazy InnerClass~
参考文献:
[ 1 ] 图解设计模式/(日)结城浩著;杨文轩译。–北京:人民邮电出版社,2017.1.
[ 2 ] 维基百科 设计模式
[ 3 ] 极客学院WIKI–设计模式.
[ 4 ] 菜鸟教程–设计模式.