單例模式:采用一定的方法,使得軟體運行中,對于某個類只能存在一個實體物件,并且該類只能提供一個取得實體的方法,
分類:
- 餓漢式
- 靜態常量方式
- 靜態代碼塊方式
- 懶漢式
- 普通方式,執行緒不安全
- 同步方法方式,執行緒安全
- 同步代碼塊方式,執行緒不安全
- 其他方式
- 雙重檢查
- 靜態內部類
- 列舉
實作思路:
-
想要實作單例,即不能讓外部隨意的去實體化物件,所以需要構造器私有
-
既然不允許外部去創建了,所以需要在類的內部創建物件
-
外部需要使用物件,所以需要對外提供一個獲取實體的方法
一、餓漢式
根據物件創建的時機,可以分為餓漢式和懶漢式,餓漢式,即在類加載的時候立即創建實體,根據所學知識我們可以很快想到要使用static關鍵字將屬性和類相關聯,以下提供兩種書寫方式供參考,
特點
- 優點:寫法簡單,在類裝載的時候就完成實體化,避免了執行緒同步問題
- 缺點:在類裝載的時候就完成了實體化,如果開始至終都沒有使用這個實體,會造成記憶體浪費
1.靜態常量方式
class Singleton01{
//構造器私有,防止外部new
private Singleton01(){
}
//內部創建物件
private final static Singleton01 instance = new Singleton01();
//提供給外部創建實體的靜態方法
public static Singleton01 getInstance(){
return instance;
}
}
2.靜態代碼塊方式
class Singleton02{
//構造器私有,防止外部new
private Singleton02(){
}
//內部創建物件
private static Singleton02 instance;
static {
instance = new Singleton02();
}
//提供給外部創建實體的靜態方法
public static Singleton02 getInstance(){
return instance;
}
}
這種方式和靜態常量的實作方式邏輯和優缺點是一樣的,只是寫法不同,
二、懶漢式
懶漢式,即在類加載時并不實體化物件,等到使用物件實體的時候才去實體化,也被稱為懶加載效果,這樣做的好處可以節約資源,減少浪費,只有需要的時候采取創建,不需要就不會實體化,
1.普通方式(執行緒不安全)
class Singleton01{
// 構造器私有
private Singleton01(){
}
// 定義靜態變數,實體化留在獲取實體的getInstance方法,起到懶加載效果
private static Singleton01 instance;
public static Singleton01 getInstance(){
// 判斷如果為空才創建,起到懶加載
if (instance == null){
instance = new Singleton01();
}
return instance;
}
}
問題:在多執行緒情況下,假設類還未第一次實體化,此時兩個行程同時執行到了if(instance==null),而未來得及往下執行時,此時兩者校驗都成立,都會執行實體化操作,將有可能出現創建多個實體的問題,
2.同步方法方式(執行緒安全)
既然存在執行緒安全問題,肯定會想到使用synchronized關鍵字來解決
class Singleton02{
private static Singleton02 instance;
private Singleton02(){
}
//使用synchronized關鍵字來實作執行緒安全
public static synchronized Singleton02 getInstance(){
if (instance == null){
instance = new Singleton02();
}
return instance;
}
}
這樣的方式可以起到執行緒安全的效果,但是每個執行緒都需要等待鎖,所以又會存在效率低的問題,于是有人想到了將鎖的范圍縮小到方法的內部,使用同步代碼塊的方式
3.同步代碼塊方式(執行緒不安全)
這樣的方式好不好呢?先看代碼
class Singleton03{
private static Singleton03 instance;
private Singleton03(){
}
public static Singleton03 getInstance(){
if (instance == null){
// 這里的synchronized其實沒有實際意義,可能會產生多個實體
synchronized (Singleton03.class){
instance = new Singleton03();
}
}
return instance;
}
}
這樣鎖的范圍是變小了,但是還會存在多個執行緒同時判斷到if (instance == null),即使在后面加上鎖,依舊會在后續創建實體,只是延遲了一點而已,所以這種寫法不可取
三、其他方式
1.雙重檢查
為了能夠實作懶加載的效果,同時兼顧效率,于是出現了這種寫法
class Singleton01{
//volatile,當有發生變化時即時儲存到記憶體中,防止指令重排
private static volatile Singleton01 instance;
private Singleton01(){
}
//雙重檢查,解決執行緒同步問題,又保證效率
public static Singleton01 getInstance(){
if (instance == null){ // 第一次檢查,降低產生鎖的概率
synchronized (Singleton01.class){
if(instance == null){ // 第二次檢查,保證執行緒安全
instance = new Singleton01();
}
}
}
return instance;
}
}
使用雙重檢查,第一次檢查提升效率,第二次檢查保證執行緒安全,簡直美滋滋
2.靜態內部類
利用靜態內部類在被呼叫時才會加載,即存在懶加載效果,所以也可以這樣寫
class Singleton02{
private Singleton02(){
}
/*
靜態內部類在外部類裝載的時候不會馬上執行,起到懶加載作用,
類的靜態屬性只有在第一次使用的時候才會加載,JVM在類加載時是執行緒安全的
*/
private static class SingletonInstance{
private static final Singleton02 INSTANCE = new Singleton02();
}
public static Singleton02 getInstance(){
return SingletonInstance.INSTANCE;
}
}
3.列舉
列舉方式是最簡單的寫法,也是被很多人推崇的寫法
enum Singleton03{
INSTANCE;
}
簡單明了...
四、小結
使用單例模式,可以使一個類只存在一個實體物件,從而節省了系統資源,
上文中列出了8個寫法,其中懶加載的寫法存在執行緒安全和效率的問題,需要謹慎使用,比較推薦的寫法有5種:懶加載2種+其他方式3種,當認定單例的物件在軟體中一定會用到,可以使用懶加載,反之可以使用其他方式
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/245135.html
標籤:其他
下一篇:SSM框架入門學習記錄
