待解决问题:
-
volatile
关键字作用 和 第三种解决多线程问题单件模式代码的理解? - 对于下图的理解
正题
首先来一个 Head First Design Patterns 中的定义:单件模式保证了某个类只有一个实例,并且这个实例提供了一个全局访问点。
什么情况需要用到呢?
线程池,缓存,对话框,日志对象,设备驱动程序等。在这些情况下,如果实例化多个对象,如果是资源敏感型的对象(访问IO或数据库资源),初始化多个对象会消耗大量资源。此外,多个对象可能会导致程序行为异常,或者产生冲突的结果。
为什么不直接用一个全局变量呢?
确实我们可以使用一个全局的静待变量实例化我们想要的对象,个人理解单件模式好处有二,其一是采用静态变量在程序刚开始运行的时候就会实例化对象,对于资源敏感型的对象不太友好;而单件模式是一种 lazy manner,只有当有需求但对象没有被创建的时候才会创建。其二如果一个项目由多人开发,很多人都需要用到这个对象,那么每个人应该有对该资源已经被创建有共识才能保证整个过程只有一个对象;而单件模式开发者不需要知道其中的细节。(第二点是自己瞎想的QAQ)
单件模式的代码?
public class Singleton {
private static Singleton uniqueInstance;
// …
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// …
}
以上代码的类图是这样的?
但是上面的代码存在一个重大 bug ! 它不是线程安全的
假设有两个线程,其中一个运行到 uniqueInstance = new Singleton();
这段代码,但还没有运行完,所以此时此刻是没有实例化的对象的,另一个线程运行到 if (uniqueInstance == null)
这段代码时会发现没有对象,所以也进入到 uniqueInstance = new Singleton();
这段代码,这种情况以上代码就不起作用了!
该如何修改呢?
第一种做法就是对 getInstance()
这个方法加上 synchronized
的修饰符,能解决问题,但是很损害性能(overhead of synchronization)
public class Singleton {
private static Singleton uniqueInstance;
// …
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// …
}
课本的建议?
第二种做法是 eager instantiation 和全局静态变量差不多,缺点就不必说了。
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
// …
private Singleton() {}
public static synchronized Singleton getInstance() {
return uniqueInstance;
}
// …
}
第三种做法是 Double checked locking (Java 5 之后才能使用) 其中volatile
关键字的作用是保证多个线程正确地处理单个示例(怎么个保证法?待研究 还有 synchronized
的写法挺奇怪的…
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton () {
}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
参考资料:
- Head First Design Patterns
- 老师上课的PPT