單例設計模式Singleton
存在意義
- 有些對象的創建消耗時間和內存是非常大的,恰恰好這些對象在我們的應用中只需要使用 1 個,如果不能得到控制,會造成資源的浪費。例如線程池、數據庫連接池,一個應用程序中,我們只需要有 1 個這樣的大對象。
單例模式的兩種寫法:懶漢式和餓漢式
說明
- 餓漢式:在程序啓動或單件模式類被加載的時候,單件模式實例就已經被創建。(線程安全)
- 懶漢式:當程序第一次訪問單件模式實例時才進行創建。(線程不安全)
如何選擇
如果單件模式實例在系統中經常會被用到,餓漢式是一個不錯的選擇。
如果單件模式在系統中會很少用到或者幾乎不會用到,那麼懶漢式是一個不錯的選擇。
懶漢式
/**
* 單例懶漢式:當需要用到的時候纔會創建實例,缺點:線程不安全
*
* @title
* @description
* @since JDK1.8
*/
public class SingleTon {
private static SingleTon single;
// 構造器私有化,不能在類的外部隨意創建對象
private SingleTon() {
}
//重點:同步不要加在方法上,加在可能發生線程不安全的地方(寫操作而不是讀操作)
public static SingleTon getSingleTon() {
if (single == null) {
// System.out.println("hello");
synchronized (SingleTon.class) {
if (single == null)
single = new SingleTon();
}
}
return single;
}
}
餓漢式
/**
* 單例模式餓漢式:class文件被加載的時候創建實例,缺點:內存消耗大
*
* @title
* @description
* @since JDK1.8
*/
public class SingleTon1 {
private static SingleTon1 single = new SingleTon1();
private SingleTon1() {
}
public static SingleTon1 getSingleTon1() {
return single;
}
}
測試類
/**
* 單例測試類
* @title
* @description
* @since JDK1.8
*/
public class Main {
public static void main(String[] args) {
// // 懶漢測試
// SingleTon single1 = SingleTon.getSingleTon();
// SingleTon single2 = SingleTon.getSingleTon();
// System.out.println(single1 == single2);
// // 餓漢測試
// SingleTon1 single3 = SingleTon1.getSingleTon1();
// SingleTon1 single4 = SingleTon1.getSingleTon1();
// System.out.println(single3 == single4);
// 多線程驗證懶漢式
// for (int i = 0; i < 10; i++) {
// Runnable run = new Runnable() {
// @Override
// public void run() {
// SingleTon single5 = SingleTon.getSingleTon();
// System.out.println(single5);
// }
// };
// Thread t = new Thread(run);
// t.start();
// }
Runnable run = () -> {
//懶漢式多線程測試
SingleTon single5 = SingleTon.getSingleTon();
System.out.println(single5);
};
for (int i = 0; i < 10; i++) {
Thread t = new Thread(run);
t.start();
}
}
}
擴展
- 單例模式用途:
單例模式屬於工廠模式的特例,只是它不需要輸入參數並且始終返回同一對象的引用。
單例模式能夠保證某一類型對象在系統中的唯一性,即某類在系統中只有一個實例。它的用途十分廣泛,打個比方,我們開發了一個簡單的留言板,用戶的每一次留言都要將留言信息寫入到數據庫中,最直觀的方法是沒次寫入都建立一個數據庫的鏈接。這是個簡單的方法,在不考慮併發的時候這也是個不錯的選擇。但實際上,一個網站是併發的,並且有可能是存在大量併發操作的。如果我們對每次寫入都創建一個數據庫連接,那麼很容易的系統會出現瓶頸,系統的精力將會很多的放在維護鏈接上而非直接查詢操作上。這顯然是不可取的。
如果我們能夠保證系統中自始至終只有唯一一個數據庫連接對象,顯然我們會節省很多內存開銷和cpu利用率。