由今日開始,本Blog 改為用中文編寫,敬請留意。
1. A simple one
public class Singleton{
private static StringBuilder sb;
public static StringBuilder getInstance(){
if(sb==null){
sb=new StringBuilder();
return sb;
}else{
return sb;
}
}
}
這個 Class 有什麼問題?
就是沒有考慮到Multi Threading Access 的問題。
假設有兩條Thread 同時 Access 一個未被Constructed 的 StringBuilder, 第一條Thread 首先Create Object 到 Heap Memory 和存放 Reference 去到Stack.
可是,第二條 Thread 並未能看見第一條Thread 提供的改動,它看到的 sb 仍是 null ,所以
在 Java 5 的打後,Java JSR-133 提供了JMM, Java Memory Model.
Hey Hey Hey, what 's that about??
在這裡,我只會簡單地介紹,有興趣的朋友可以到
JSR 133 (Java Memory Model) FAQ
http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#otherlanguages其中一個重點是提供了 volatile modifier.
它提供了一個溝通的機制,令到Thread 1 能夠和Thread 2 溝通,Make sure Thread 1 的 Changes 會被Thread 2 看見。
Volatile 的出現,令到我們可以寫一個Thread safe 的 Singleton, Double Checked Singleton.
Double Checked Singleton
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
}
假設有兩條Thread 先後執行 getHelper(), 當Thread 1 進入時,會首先Check 一下 Helper 是否為null,
由於 synchronized 去拿個java monitor 的時間會大大影響程式的運行速度,所以我們會將 null checking 放在第一個if。
接著,Thread 1 從 synchronized 取得lock, 再以null checking determine 一下 helper 是null or not.
最後才創建 Helper.
Volatile 的作用是在創建了 Helper 之後,Thread 1 令到 Thread 2 知道 helper 己經被創建。
第二種方法:
使用Enum:(參考 Effective Java Edition 2 , Joshua Bloch)
public enum Singleton {
INSTANCE;
public void doStuff(String stuff) {
System.out.println(stuff);
}
}
這種方法的Call 法非常簡單, Singleton.INSTANCE.doStuff("abc"), 便call 了 Singleton 一次了。
但這種的缺點就是它會隨着Class Loader load 起的時候,裏面的Obect 便 Constructed 了起來,沒有了Lazy Initialized 的感覺。
第三種方法:
Initialization-on-demand holder idiom(From University of Maryland Computer Science researcher Bill Pugh)
public class Something {
private Something() {
}
private static class LazyHolder {
private static final Something INSTANCE = new Something();
}
public static Something getInstance() {
return LazyHolder.INSTANCE;
}
}
這個Solution 能夠有Lazy initialization 的是因為 Inner Class 是只會在第一次存取時才會被JVM Initialize ,
所以便逹到了 Lazy 的效果。再者,Class Initialization 是 non-concurrent 的,所以並不需要Sycnhronization 的幫助來逹至Thread Safe 的效果。
看似這是我在大學時的Software Engineering 學過的, 可以Provide 一個Global Point of Access and 省卻記憶體和物件Create 的時間.
什麼可以係Singleton 呢?
1. Datasource
2. Cache Instance
Singleton 缺點:
1. Per Class loader wise(不是Unique in every JVM, 因為可以有數個同一個Singleton 的Class)
2. Unit Testing, 因為Singleton是只會Initalize 一次,跟着便放在 Memory 內,難以被Mock or subclass override.
參考:
1. Singleton Pattern
2. Effective Java 2nd Ed
3. Java Concurrency (&c)
4. Patterns I hate
期待下一次對大家分享其他Knowledge 和工作經驗!
希望下次可以講一下ORM, 以及 Persistence Polygot...哈哈,要加油喔.
有錯請多多指教,謝!