### 單例模式
定義:確保某一個類只有一個實例,自行實例化并且向整個系統提供這個實例。
使用場景:避免某個類產生多個對象而消耗過多的資源,確保某個類在程序中只有一個實例;
單例模式的優點:
* 對于頻繁使用的對象,可以省略new操作花費的時間,這對于那些重量級對象而言,是非常可觀的一筆系統開銷;
* 由于new操作的次數減少,因而對系統內存的使用頻率也會降低,這將減輕GC的壓力,縮短GC停頓時間;
#### 單例實現方式
**方法一\(懶漢式\):**
```
public class Singleton {
private Singleton(){
}
private static Singleton singleton = new Singleton();
public Singleton getInstance() {
return singleton;
}
}
```
說明:簡單正確,但Singleton實例創建時機不受控制,任何對Singleton方法或字段的的引用都會導致類的初始化并創建instance實例,但是類的初始化只有一次,因此instance實例永遠只會被創建一次;但是如果程序從頭到位都沒用使用這個單例的話,單例的對象還是會創建。這就造成了不必要的資源浪費
**方法二\(餓漢式\):**
```
public class LazySingleton {
private LazySingleton(){
}
private static LazySingleton instance = null;
public static synchronized LazySingleton getInstance() {
if (null == instance) {
instance = new LazySingleton();
}
return instance;
}
}
```
說明:并發場景下會有性能損耗
**方法三\(雙重加鎖DCL的實現方式,不推薦\):**
```
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (null == instance) {
synchronized (LazySingleton.class) {
if (null == instance) {//增加volatile后instance被實例化后立即可見
instance = new LazySingleton();
}
}
}
return instance;
}
}
```
這段代碼只有一個問題 —— 它不能正常工作\(甚至被稱為“臭名昭著的雙重鎖檢查”\);最明顯的原因是,初始化實例的寫入操作和實例字段的寫入操作能夠被編譯器或者緩沖區重排序,重排序可能會導致返回部分構造的一些東西。就是我們讀取到了一個沒有初始化的對象。這段代碼還有很多其他的錯誤,以及為什么對這段代碼的算法修正是錯誤的。在舊的java內存模型下沒有辦法修復它
“_許多人認為使用volatile關鍵字能夠消除雙重鎖檢查模式的問題。在1.5的JVM之前,volatile并不能保證這段代碼能夠正常工作(因環境而定)。在新的內存模型下,實例字段使用volatile可以解決雙重鎖檢查的問題,因為在構造線程來初始化一些東西和讀取線程返回它的值之間有happens-before關系。_
_然后,對于喜歡使用雙重鎖檢查的人來說(我們真的希望沒有人這樣做),仍然不是好消息。雙重鎖檢查的重點是為了避免過度使用同步導致性能問題。從java1.0開始,不僅同步會有昂貴的性能開銷,而且在新的內存模型下,使用volatile的性能開銷也有所上升,幾乎達到了和同步一樣的性能開銷。因此,使用雙重鎖檢查來實現單例模式仍然不是一個好的選擇。(修訂—在大多數平臺下,volatile性能開銷還是比較低的)_” ——引用自《[http://ifeve.com/jmm-faq-dcl/》](http://ifeve.com/jmm-faq-dcl/》)
**方法四\(推薦\):靜態內部類**
既能享受類加載確保線程安全帶來的便利,又能延遲加載的方式,就是靜態內部類。Java靜態內部類的特性是,加載的時候不會加載內部靜態類,使用的時候才會進行加載。而使用到的時候類加載又是線程安全的
Initialization Demand Holder\(IoDH\)實現單例
```
public class StaticSingleton {
private StaticSingleton(){
}
private static class StaticSingletonHandler {
public static StaticSingleton instance = new StaticSingleton();
}
public static StaticSingleton getInstance() {
return StaticSingletonHandler.instance;
}
}
```
由于靜態單例對象沒有作為Singleton的成員變量直接實例化,因此類加載時不會實例化Singleton,第一次調用getInstance\(\)時將加載內部類HolderClass,在該內部類中定義了一個static類型的變量instance,此時會首先初始化這個成員變量,由Java虛擬機來保證其線程安全性,確保該成員變量只能初始化一次。由于getInstance\(\)方法沒有任何線程鎖定,因此其性能不會造成任何影響
通過使用IoDH,我們既可以實現延遲加載,又可以保證線程安全,不影響系統性能,不失為一種最好的Java語言單例模式實現方式
優點:
1.getInstance\(\)方法中沒有鎖,高并發場景下性能優越;
2.getInstance\(\)方法被調用時,StaticSingleton實例才被初始化;
**方法五:枚舉**
在《Effective Java》最后推薦了這樣一個寫法,簡直有點顛覆,不僅超級簡單,而且保證了線程安全;JDK1.5提供了一個新的數據類型枚舉。枚舉的出現提供了一個較為優雅的方式取代以前大量的static final類型的變量
對于一個標準的enum單例模式,最優秀的寫法還是實現接口的形式:
```
// 定義單例模式中需要完成的代碼邏輯
public interface MySingleton {
void doSomething();
}
public enum Singleton implements MySingleton {
INSTANCE {
@Override
public void doSomething() {
System.out.println("complete singleton");
}
};
public static MySingleton getInstance() {
return Singleton.INSTANCE;
}
}
```
- java演變
- JDK各個版本的新特性
- JDK1.5新特性
- JDK1.6新特性
- JDK1.7新特性
- JDK1.8新特性
- JAVA基礎
- 面向對象特性
- 多態
- 方法重載
- 方法重寫
- class
- 常量
- 訪問修飾符
- 類加載路徑
- java-equals
- 局部類
- java-hashCode
- Java類初始化順序
- java-clone方法
- JAVA對象實例化的方法
- 基礎部分
- JAVA基礎特性
- JAVA關鍵字
- javabean
- static
- 日期相關
- final
- interface
- 函數式接口
- JAVA異常
- 異常屏蔽
- try-with-resource資源泄露
- JAVA引用
- WeakReference
- SoftReference
- PhantomReference
- 位運算符
- try-with-resource語法糖
- JDK冷知識
- JAVA包裝類
- JAVA基本類型與包裝類
- java.lang.Boolean
- java.lang.Integer
- java.lang.Byte
- java.lang.Short
- java.lang.Long
- java.lang.Float
- java.lang.Double
- java.lang.Character
- 日期相關
- TemporalAdjusters
- String
- 字符串常量池
- String拼接
- String編譯期優化
- StringBuilder&StringBuffer
- intern
- 注解
- java標準注解
- 內置注解
- 元注解
- 自定義注解
- 注解處理器
- JVM注解
- Java8 Annotation新特性
- 反射-Reflective
- Reflection
- Class
- Constructor
- Method
- javabean-property
- MethodHandles
- 泛型
- 類型擦除
- bridge-method
- Accessor&Mutator方法
- enum
- JAVA數組
- finalize方法
- JAR文件
- JAVA高級編程
- CORBA
- JMX
- SPI
- Java SPI使用約定
- ServiceLoader
- 實際應用
- IO
- 工具類
- JDK常用工具類
- Objects
- System
- Optional
- Throwable
- Collections
- Array
- Arrays
- System
- Unsafe
- Number
- ClassLoader
- Runtime
- Object
- Comparator
- VarHandle
- 數據結構
- 棧-Stack
- 隊列(Queue)
- Deque
- PriorityQueue
- BlockingQueue
- SynchronousQueue
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- ConcurrentLinkedQueue
- 列表
- 迭代器
- KV鍵值對數據類型
- HashMap
- TreeMap
- Hash沖突
- ConcurrentHashMap
- JDK1.7 ConcurrentHashMap結構
- jdk7&jdk8區別
- 集合
- Vector
- Stack
- HashSet
- TreeSet
- ArrayList
- LinkedList
- ArrayList && LinkedList相互轉換
- 線程安全的集合類
- 集合類遍歷性能
- 并發容器
- CopyOnWriteArrayList
- ConcurrentHashMap
- 同步容器
- BitMap
- BloomFilter
- SkipList
- 設計模式
- 設計模式六大原則
- 單例模式
- 代理模式
- 靜態代理
- 動態代理
- JDK動態代理
- cglib動態代理
- spring aop
- 策略模式
- SpringAOP策略模式的運用
- 生產者消費者模式
- 迭代器模式
- 函數式編程
- 方法引用
- 性能問題
- Lambda
- Lambda類型檢查
- Stream
- findFirst和findAny
- reduce
- 原始類型流特化
- 無限流
- 收集器
- 并行流
- AOP
- 靜態織入
- aspect
- aspect的定義
- AspectJ與SpringAOP
- 動態織入
- 靜態代理
- 動態代理
- JDK動態代理
- CGLib動態代理
- Spring AOP
- SpringAOP五種通知類型
- @Before
- @AfterReturning
- @AfterThrowing
- @After
- @Around
- Aspect優先級
- SpringAOP切點表達式
- within
- execution
- 嵌套調用
- 系統優化與重構
- 重疊構造器模式
- 工具類構造器優化
- 常見面試題
- new Object()到底占用幾個字節
- 訪問修飾符
- cloneable接口實現原理
- 異常分類以及處理機制
- wait和sleep的區別
- 數組在內存中如何分配
- 類加載為什么要使用雙親委派模式,有沒有什么場景是打破了這個模式
- 類的實例化順序
- 附錄
- JAVA術語
- FAQ
- 墨菲定律
- 康威定律
- 軟件設計原則
- 阿姆達爾定律
- 字節碼工具
- OSGI