导航
导航
文章目录
  1. 1、饿汉式
  2. 2、lazy-load延迟加载,不线程安全
  3. 3、加方法锁
  4. 4、加代码块锁
  5. 5、双重检测代码块同步锁
  6. 6、双重检测改进
  7. 7、最佳写法:initialization on demand holder

设计模式-单例模式

1、饿汉式

1
2
3
4
5
6
7
8
9
public class Singleton {
private static Singleton instance = new Singleton();

private Singleton() {}

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

线程安全,但容易导致启动较慢。
在《Java与模式》中,作者提出:“饿汉式单例类可以在Java语言实现,但不易在C++内实现,因为静态初始化在C++里没有固定的顺序,因而静态的instance变量的初始化与类的加载顺序没有保证,可能会出问题。这就是为什么GoF在提出单例类的概念时,举的例子是懒汉式的。他们的书影响之大,以致Java语言中单例类的例子也大多是懒汉式的。实际上,本书认为饿汉式单例类更符合Java语言本身的特点。”

2、lazy-load延迟加载,不线程安全

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private static Singleton instance;

private Singleton() {}

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

3、加方法锁

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private static Singleton instance;

private Singleton() {}

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

线程安全。
但较为耗费资源。因为每个线程调用getInstance()都要加锁,我们想要只在第一次调用getInstance()时加锁。

4、加代码块锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {
private static Singleton instance;

private Singleton() {}

public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}

可能两个线程并发进入if判断语句,在一个new后,另一个线程也直接进入new。线程不安全。

5、双重检测代码块同步锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
private static Singleton instance;

private Singleton() {}

public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

二次检查可以解决4中的问题,但是,“双重检查锁定背后的理论是完美的。不幸地是,现实完全不同。双重检查锁定的问题是:并不能保证它会在单处理器或多处理器计算机上顺利运行。双重检查锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的‘无序写入’,这也是这些习语失败的一个主要原因。”
因为存在指令重排序问题,所以该方法也是线程不安全的。

6、双重检测改进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton {
private static Singleton instance;

private Singleton() {}

public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
Singleton temp = instance;
if (temp == null) {
instance = new Singleton();
}
}
}
return instance;
}
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
private static volatile Singleton instance;

private Singleton() {}

public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

volatile修饰可以确保 instance = new Singleton(); 对应的指令不会重排序

7、最佳写法:initialization on demand holder

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private Singleton () {}

// 延迟加载实例
private static class SingletonHolder {
static Singleton instance = new Singleton();
}

public static Singleton getInstance() {
return SingletonHolder.instance;
}
}

Java机制规定,内部类SingletonHolder只有在getInstance()方法第一次调用的时候才会被加载(实现了lazy),而且其加载过程是线程安全的。内部类加载的时候实例化一次instance。

参考:
http://www.iteye.com/topic/537563
http://www.jb51.net/article/46922.htm
http://www.cnblogs.com/xudong-bupt/p/3433643.html
《Spring实战》