差異處
這裏顯示兩個版本的差異處。
Both sides previous revision 前次修改 下次修改 | 前次修改 | ||
java:effective_java:concurrency:use_lazy_initialization_judiciously [2020/02/02 13:01] tony |
java:effective_java:concurrency:use_lazy_initialization_judiciously [2023/06/25 09:48] (目前版本) |
||
---|---|---|---|
行 25: | 行 25: | ||
public static Singleton get() { | public static Singleton get() { | ||
Singleton result = instance; | Singleton result = instance; | ||
- | if( instance == null ) { | + | if( result == null ) { |
result = instance = new Singleton(); | result = instance = new Singleton(); | ||
} | } | ||
行 47: | 行 47: | ||
</code> | </code> | ||
這做法的缺點就是在初始化之後,每一個Thread要存取時,都會受到這個synchronized的影響而慢一點。 | 這做法的缺點就是在初始化之後,每一個Thread要存取時,都會受到這個synchronized的影響而慢一點。 | ||
- | =====Double-check idiom ===== | + | ===== Double-check idiom ===== |
雙重檢查鎖定模式的做法,能解決使用Synchronized Method效能的問題,又能確保只會初始化一次: | 雙重檢查鎖定模式的做法,能解決使用Synchronized Method效能的問題,又能確保只會初始化一次: | ||
<code java> | <code java> | ||
+ | public class Singleton { | ||
+ | |||
+ | private static Singleton instance; | ||
+ | |||
+ | public static Singleton get() { | ||
+ | Singleton result = instance; | ||
+ | if( result != null ) { // no locking | ||
+ | return result; | ||
+ | } | ||
+ | |||
+ | synchronized(Singleton.class) { | ||
+ | result = instance; | ||
+ | if( result != null ) { // with locking | ||
+ | return result; | ||
+ | } | ||
+ | |||
+ | result = instance = new Singleton(); | ||
+ | |||
+ | } | ||
+ | return result; | ||
+ | } | ||
+ | } | ||
</code> | </code> | ||
+ | 第一次的instance檢查不會受lock控管,只要在第一次初始化後,就能夠使用;第二次的instance檢查是為了避免Multi-thread情況下,重複初始化的問題。這個方法適用於非static的欄位,我是都以static當例子較簡單。(我的程式碼與effective java有落差,因為我認為書內的程式在with locking的檢查並沒assign instance給result,一定會有問題) | ||
+ | ===== Lazy initialization holder class idiom ===== | ||
+ | Lazy initialization holder class的做法,之前在第二版的時候並沒有特別注意,直到我看Java高併發編程詳解:多線程與架構設計這本書時,才意會到這寫法使用在static欄位初始化會比double-check idiom方式簡單很多。程式碼如下: | ||
+ | <code java> | ||
+ | public final class Singleton { | ||
+ | private static class Holder { | ||
+ | static final Singleton instance = new Singleton(); | ||
+ | } | ||
+ | |||
+ | public static Singleton get() { | ||
+ | return Holder.instance; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 這一樣是透過ClassLoader載入Class Thread-Safe的機制,來解決同步存取的問題。目前這是要初始化static欄位的首選。 | ||
+ | ===== Enum method ===== | ||
+ | 這方法是看Java高併發編程詳解:多線程與架構設計這本書時,有提到透過Enum去解這問題是Effective Java作者力推的方法,在Item89有提及。在這我就稍微說明Enum method的做法: | ||
+ | <code java> | ||
+ | public enum Singleton { | ||
+ | |||
+ | INSTANCE; | ||
+ | |||
+ | public static Singleton get() { | ||
+ | return INSTANCE; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | Enum擁有Thread-safe、只能被初始化一次且不能夠被繼承的特性;缺點就是無法做Lazy initialization,要解決這問題可以與Holder方式結合。但與其這樣,不如使用Holder就好。 | ||
+ | |||
+ | |||
===== Note ===== | ===== Note ===== | ||
Effective Java第三版Item 83。 | Effective Java第三版Item 83。 | ||
===== Reference ===== | ===== Reference ===== | ||
* Java Threads, 3/e | * Java Threads, 3/e | ||
+ | * Effective Java, 3/e | ||
* Java高併發編程詳解:多線程與架構設計, 汪文君 | * Java高併發編程詳解:多線程與架構設計, 汪文君 | ||
* [[https://zh.wikipedia.org/wiki/%E5%8F%8C%E9%87%8D%E6%A3%80%E6%9F%A5%E9%94%81%E5%AE%9A%E6%A8%A1%E5%BC%8F|雙重檢查鎖定模式]] | * [[https://zh.wikipedia.org/wiki/%E5%8F%8C%E9%87%8D%E6%A3%80%E6%9F%A5%E9%94%81%E5%AE%9A%E6%A8%A1%E5%BC%8F|雙重檢查鎖定模式]] |