設計模式—singleton(單例模式)
阿新 • • 發佈:2021-03-26
**單例模式**
* 單例設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。這種模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有單個物件被建立。
這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要例項化該類的物件。
**應用例項**
* Windows是多程序多執行緒的,通過唯一的例項來操作一個檔案,避免地出現多個程序或執行緒同時操作一個檔案的現象。
* 一些裝置管理器常常設計為單例模式(如一個電腦有兩臺印表機,在輸出的時候就要處理不能兩臺印表機列印同一個檔案)。
**使用場景**
* 要求生產唯一序列號。
* WEB 中的計數器,不用每次重新整理都在資料庫里加一次,用單例先快取起來。
* 建立的一個物件需要消耗的資源過多,比如 I/O 與資料庫的連線等。
**單例實現**
* 方式一
![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210322115250808-867812575.png)
**方式二**
* 改進了方式一的缺點,但同時帶來了執行緒安全問題
* 假設有兩個執行緒,執行緒1到第9行判斷完例項是否為空時(還沒到new一個例項),執行緒2也到了判斷例項是否為空的位置,
因為執行緒1還沒建立例項,INSTANCE仍為空,執行緒2建立完一個例項後,執行緒1繼續執行也建立了一個例項。
![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210322172430367-1939051568.png)
**方式三**
* 通過synchronized加鎖,解決方式二的執行緒安全問題,但也帶來了效率下降。
* 通過加鎖,鎖定了Mgr03.class物件,因此輸出的hashcode是相同的。
![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210323201308506-2037982314.png)
**方式四**
* 雙重校驗鎖,完美的單例模式寫法之一。
* 如果只有外層判斷,就會出現執行緒安全問題(如同方式二)。
![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210323203033410-406511331.png)
**方式五**
* 靜態內部類方式,解決了方式一的問題,達到了按需初始化的目的,是完美的單例模式之一。
![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210323203738300-1495577083.png)
**方式六**
* 通過列舉實現單例
![](https://img2020.cnblogs.com/blog/2282070/202103/2282070-20210323204330719-741625910.png)
* 列舉方式 [https://blog.csdn.net/moakun/article/details/80688851]()
```
public class Mgr05 {
private Mgr05() {};
//類Mgr05載入時,內部類不會載入
private static class Mgr05Holder{
private final static Mgr05 INSTANCE=new Mgr05();
}
//呼叫getInstance(),類Mgr05Holder載入
public static Mgr05 getInstance() {return Mgr05Holder.INSTANCE;};
public static void main(String[] args) {
for(int i=0;i<200;i++) {
new Thread(()->{
System.out.println(Mgr05.getInstance().hashCode());
}).start();
}
}
}
```
```
public class Mgr04 {
private static volatile Mgr04 INSTANCE;
private Mgr04() {};
public static synchronized Mgr04 getInstance() {
if(INSTANCE==null) {//雙重校驗鎖
synchronized (Mgr04.class) {
if(INSTANCE==null) {
try {
Thread.sleep(10);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
INSTANCE=new Mgr04();
}
return INSTANCE;
}
public void c() {System.out.println("c");}
public static void main(String[] args) {
for(int i=0;i<200;i++) {
new Thread(()->{
System.out.println(Mgr04.getInstance().hashCode());
}).start();
}
}