當前位置:
首頁 > 知識 > 戲說設計模式 只有一個中國 單例模式

戲說設計模式 只有一個中國 單例模式

戲說設計模式 - 單例模式

[toc]

簡介

Singleton

保證類有且僅有一個實例,並提供一個訪問它的全局站點

類型

餓漢模式

懶漢模式

懶漢模式 PK 餓漢模式

優缺點

優點

由於單例模式在內存中只有一個實例,

減少了內存開支

,特別是一個對象需要頻繁地創建、銷毀時,而且創建或銷毀時性能又無法優化,單例模式的優勢就非常明顯。

由於單例模式只生成一個實例,所以

減少了系統的性能開銷

,當一個對象的產生需要比較多的資源時,如讀取配置、產生其他依賴對象時,則可以通過在應用啟動時直接產生一個單例對象,然後用永久駐留內存的方式來解決(在Java EE中採用單例模式時需要注意JVM垃圾回收機制)。

單例模式可以

避免對資源的多重佔用

,例如一個寫文件動作,由於只有一個實例存在內存中,避免對同一個資源文件的同時寫操作。

單例模式可以在系統設置全局的訪問點,

優化和共享資源訪問

,例如可以設計一個單例類,負責所有數據表的映射處理。

缺點

單例模式一般沒有介面,

擴展很困難

,若要擴展,除了修改代碼基本上沒有第二種途徑可以實現。單例模式為什麼不能增加介面呢?因為介面對單例模式是沒有任何意義的,它要求「自行實例化」,並且提供單一實例、介面或抽象類是不可能被實例化的。當然,在特殊情況下,單例模式可以實現介面、被繼承等,需要在系統開發中根據環境判斷。

單例模式

對測試是不利的

。在並行開發環境中,如果單例模式沒有完成,是不能進行測試的,沒有介面也不能使用mock的方式虛擬一個對象。

單例模式

與單一職責原則有衝突

。一個類應該只實現一個邏輯,而不關心它是否是單例的,是不是要單例取決於環境,單例模式把「要單例」和業務邏輯融合在一個類中。

適用場景

應用程序中的該對象有且只有一個即可

創建一個對象需要消耗的資源過多,如要訪問IO和資料庫等資源;

需要定義大量的靜態常量和靜態方法(如工具類)的環境,可以採用單例模式(當然,也可以直接聲明為static的方式)。

注意事項

在高並發情況下,請注意單例模式的線程同步問題。

案例

只有一個中國

小鳥:「該死的日本鬼子,...」

大牛:「小鳥,你突然罵日本人幹啥?」

小鳥:「連一個懷孕六個月的孕婦都不放過,居然將刺刀插進孕婦的腹部,挑出一個還沒出世的孩子,好好的一個南京城,硬是被他們弄的血流成河.」

大牛:「你在看《南京大屠殺》吧!在那個貧窮積弱的年代,又何止是一個南京,整個華夏大地都被他們弄的烏煙瘴氣,幸好,咱們的前輩們站起來了,打贏了那一場硬仗。」

小鳥:「是啊!勝利來之不易,所以我們要堅持只有一個中國的原則。到現在了,居然還有人鬧藏獨,台獨,真想化身成歐特曼,鋼鐵俠,誰敢不服,誰敢鬧分裂,就揍誰!」

大牛:「哈哈,想不到我們小鳥還是一個這麼有血性的人,不過大象是不會去計較螞蟻的挑釁的。」

大牛:「香港回歸,你能用程序實現嗎?」

小鳥:「我編寫一個中國類,定義一個城市返回的方法,這裡定義為帶參的(參數:城市),因為我們都知道,香港回歸後,有澳門回歸,我更期待不久的將來,由台灣回歸.這是事實的角度出發,同時也是從系統的擴展性出發」

小鳥:「客戶端調用,香港回歸」

China china = new China(); china.returnChina("香港");

大牛:「小鳥,說的不錯,擴展性你已經考慮到了,但是就像你前面說的勝利來之不易,我們要堅持只有一個中國的原則,而你這裡有體現沒?」

小鳥:「沒有,我的中國類是new出來的,如果我不new的話,我就沒法創建對象了啊」

大牛:「你可以將它的構造方法私有化啊。」

懶漢模式實現

私有化構造方法

:限制產生多個對象

小鳥:「

構造方法私有化

,是將構造方法的修飾符改成private嗎?」

private China() { }

大牛:「是的。」

創建類的唯一實例

:創建中國類的唯一實例,並使用private static 修飾

小鳥:「這樣改了,我外部就沒法實例化了哦,不要說台灣回歸,就是已經回歸了的香港,澳門,都回歸不了啊?」

大牛:「private的作用域是?」

小鳥:「在其類的內部是可以使用的,我明白了,你是說讓我在中國類裡面聲明一個自己的對象,然後通過get方法讓它自行實例化.同時也是為了提供給外部使用」

大牛:「不錯不錯,一點就通,不過能將代碼寫出來,才表示真正理解了哦!」

小鳥:「這還不容易」

單例對象:China

類中其它方法盡量是static

public class China { // 自行實例化 private static China china; private China() { } // 返回我自己的對象 public static China getChina() { if (china == null) { china = new China(); } return china; } public void returnChina(String city) { System.out.println(city + "回歸"); } }

客戶端

China china = China.getChina(); china.returnChina("香港");

大牛:「不錯,這裡還有一點需要知道的就是,

類中的其它方法,盡量是static

小鳥:「ok,我馬上把returnChina改成static修飾.」

餓漢模式實現

大牛:「這裡還有一個問題需要說明一下,就是關於

並發

,會導致線程不安全。」

小鳥:「並發?」

大牛:「換句話說吧!假設我們的香港和澳門是同一天同一時刻回歸的,那麼也就代表會同時去訪問China類 ---> 由於開始並沒有任何城市訪問過China類,所以會導致它們都進入getChina()方法中的if語句裡面 ---> 這樣的話就會造成我們的china會new兩次。」

小鳥:「我明白了,我們這個單例模式,就是為了保證整個應用程序中有且只有一個該對象,像開始那種低並發的情況,我那樣寫是沒什麼問題的,但是如果要考慮高並發的話,就不能這麼寫了,如果不這麼寫,我又該如何寫呢?」

大牛:「不錯我們的小鳥還挺善於總結的,關於並發導致線程不安全,我們這裡有幾種解決方案。」

第1種:synchronized

在getChina方法前加synchronize關鍵字

synchronize作用:用於解決線程同步的問題, eg:A,B同時訪問方法c, 如果不加synchronize,則同時訪問,如果加了B會等A訪問完在進行訪問

大牛:「第1種,就是在getChina方法前加synchronized,它的作用就是香港,澳門同一時刻回歸時,它們種任意一個獲取完中國對象,另一個才會去獲取對象,也就解決了會同時產生兩個中國對象的問題.」

public static synchronized China getChina() { if (china == null) { china = new China(); } return china; }

第2種:雙重鎖定

Double-Check Locking

小鳥:「如果是這樣的話,確實是能解決同時產生兩個中國對象的問題,但是每次獲取China對象時,都會進行鎖定,這樣不好吧!會影響性能吧?」

大牛:「確實,分析的不錯,所以這裡我就給你講一下第2種實現方式:雙重鎖定,具體代碼如下:」

public static China getChina() { if (china == null) { synchronized (new Object()) { if (china == null) { china = new China(); } } } return china; }

大牛:「我們這裡在加鎖前面進行了一次判斷,所以就解決了我們每次都要進行加鎖操作的問題,我們再加鎖的代碼塊中再次進行判斷,就解決了我們會產生兩個對象的問題.」

第3種:餓漢模式

小鳥:「如果是這樣的話,確實是能解決同時產生兩個中國對象的問題,但是我們假設當前第1個回歸的是香港,香港正在獲取中國這個對象,但出了意外,大不列顛及北愛爾蘭聯合王國政府欠揍,突然反悔,那麼我們的香港就會一直處於查找中國的階段,由於我們加了synchronized關鍵字,它會解決並發的問題,對線程進行一個排序,從而也就導致我們的香港如果不回歸的話,我們的澳門也就不能回歸了,這樣是不是太虧了啊!」

大牛:「分析的不錯,確實會有這個問題,所以我們這裡推薦第3種實現方式,餓漢模式,即在類進行裝載的時候,就對其進行實例化.」

public class China { // 自行實例化 private static final China china = new China(); private China() { } // 返回我自己的對象 public static China getChina() { return china; } // 類中其它方法,建議用static修飾 public static void returnChina(String city) { System.out.println(city + "回歸"); } }

小鳥:「這樣就完美了!」

大牛:「非也,非也,我們這3種方式都是從線程安全的角度來考慮,第1種:線程安全,但是嚴重影響性能;第2種:線程安全,性能優於第1種;第3種:線程安全,性能優於前面兩種,但是由於類裝載的時候,就進行了唯一實例的初始化,會導致內存的浪費。」

小鳥:「那我採用哪種比較優呢?」

大牛:「我們一般是採用第3種,內存浪費點就浪費點,現在設備都不差這點內存,最主要的是運行時,由於一開始已經載入,所以運行速度會快一點。」


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 java學習吧 的精彩文章:

java學習 JavaScript學習心得
高級java程序員必備的面試官常問的知識
Spring MVC異常處理詳解
Java 小白比較關心的幾個問題

TAG:java學習吧 |

您可能感興趣

一個有趣的鉤針模式
單例模式的特點
遊戲玩了二十年,只有兩種模式,而這種新模式讓虛擬遊戲堪比現實
單例模式介紹
除了故事模式,《靈魂能力6》還包含另一個單人模式
設計模式概念
共享工作模式這個噱頭,了解一下
穿越火線裡面一直存在這樣的一個模式,但大多數人沒辦法玩!
契約模式不管其中一個英雄選誰,另外一個是這五個之一就能贏!
這是買手店設計,更是一種商業模式!
介紹一種訓練模式——IFT模型
24種設計模式及案例
刺激戰場:玩膩了經典模式,你有沒有玩過這幾種模式!
圖層調色法,調色的另一種模式,這個教程就兩個字:簡單
一切的婚外情,只有這一種模式
一個新的模式你一定不要錯過
第三方影像中心是一個好的商業模式嗎?
最強設計模式全攻略,看這一篇文章就夠了
來了解一下單反的幾種模式
設計模式之代理模式