JAVA设计模式——Singleton单例

本文的设计模式来源于github上人气比较高的iluwatar/java-design-patterns,包含了23种经典设计模式和很多实际应用中涉及到的模式。

文中涉及到的示例代码可能只抽取了部分进行讲解,如果有本地模拟需求,请参考源码。

“设计模式是程序员在设计应用程序时能够解决场景问题的最佳实现,通过经测试和验证的开发范例,可以提高开发效率。重用设计模式,可有效避免可能因细微问题而导致的重大隐患,同时有助于提升熟悉设计模式的编码人员和架构师对代码的可读性。”

作用

单例模式Singleton),保证了一个类只会存在一个支持全局访问的实例。

适用性

以下场景下可以考虑使用单例模式:

  1. 一个类需要保证只有一个实例,同时这个实例需要为其他调用者提供统一的调用方式,如getInstance;
  2. 当一个实例需要通过子类进行扩展时,其调用者能够直接使用扩展后的实例而不用修改代码;

典型用例

  1. 日志记录类
  2. 数据库连接管理
  3. 文件管理器

代码示例

本文提供常见的几种实现方式,均属于线程安全的。

饿汉式

不论该对象会不会被使用,均进行实例化。缺陷:对于系统中不会被使用的单例,造成了一定的内存浪费。

1
2
3
4
5
6
7
8
public final class IvoryTower {
private IvoryTower() {
}
private static final IvoryTower INSTANCE = new IvoryTower();
public static IvoryTower getInstance() {
return INSTANCE;
}
}

懒汉式

首次调用时才进行实例化。缺陷:每次调用均需要检查线程同步,一定程度上降低了性能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public final class ThreadSafeLazyLoadedIvoryTower {
private static ThreadSafeLazyLoadedIvoryTower instance;
private ThreadSafeLazyLoadedIvoryTower() {
if (instance == null) {
instance = this;
} else {
throw new IllegalStateException("Already initialized.");
}
}
// 每次实例请求都会进行线程同步锁
public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() {
if (instance == null) {
instance = new ThreadSafeLazyLoadedIvoryTower();
}
return instance;
}
}

静态内部类实例化

通过静态内部类完成实例化过程,保证在首次调用时才进行实例化的同时也属于线程安全的。

1
2
3
4
5
6
7
8
9
10
11
public final class InitOnDemandHolderIdiom {
private InitOnDemandHolderIdiom() {
}
public static InitOnDemandHolderIdiom getInstance() {
return HelperHolder.INSTANCE;
}
// 注意:静态内部类的加载是在首次使用时才触发。
private static class HelperHolder {
private static final InitOnDemandHolderIdiom INSTANCE = new InitOnDemandHolderIdiom();
}
}

双重检查锁

引用类如果为空时才进行线程同步判断,解决了懒汉式的多余线程同步判断问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final class ThreadSafeDoubleCheckLocking {
private static volatile ThreadSafeDoubleCheckLocking instance;
private ThreadSafeDoubleCheckLocking() {
if (instance != null) {
throw new IllegalStateException("Already initialized.");
}
}
public static ThreadSafeDoubleCheckLocking getInstance() {
ThreadSafeDoubleCheckLocking result = instance;
if (result == null) {
synchronized (ThreadSafeDoubleCheckLocking.class) {
result = instance;
if (result == null) {
instance = result = new ThreadSafeDoubleCheckLocking();
}
}
}
return result;
}
}

枚举实现

枚举中定义方法,要求枚举构造函数必须是私有化的,同时枚举实例一定是static final的,因此可利用这些特性实现单例。该方式实际使用的没有前三种多,但是不失为一个更简洁的单例实现方式。

1
2
3
4
5
6
7
public enum EnumIvoryTower {
INSTANCE;
@Override
public String toString() {
return getDeclaringClass().getCanonicalName() + "@" + hashCode();
}
}

类图