作者:solo陳,轉(zhuǎn)載請注明出處盔腔。
個人主頁:http://www.reibang.com/users/5c2177416a84/latest_articles
??設(shè)計模式是你在學習java道路上必須要學會掌握的杠茬,當然也并不是24種設(shè)計模式你都要掌握得很透徹。下面列出幾項:《設(shè)計模式的好處》
一弛随、有助于設(shè)計系統(tǒng)架構(gòu)瓢喉、增強系統(tǒng)健壯性、利于維護
二舀透、有利于加強思考問題的思維栓票、思考能力
三、有利于自己的編寫代碼愕够、閱讀代碼的能力
??當然你在編碼設(shè)計過程中如果只是為了顯擺一下你會某種設(shè)計模式這個是沒必要的走贪,因為過多的使用并不會給程序帶來良好的閱讀性,反而會增加系統(tǒng)的復雜度惑芭,所以應(yīng)該因地制宜坠狡。其實如何在正確的地方正確的使用設(shè)計模式是初學者普遍會問的問題。比如LZ當時就會有這個的問題:學了單例模式遂跟,自己也會寫逃沿,但是在什么情況下使用呢?這個就不得而知了幻锁,導致自己很多時候都很迷茫自己究竟有沒有學習設(shè)計模式的必要凯亮。所以LZ寫這個文章的初衷是將工作中使用到的結(jié)合起來進行說明:
??下面先來說說單例模式。單例模式不論在自己編碼或者源代碼中都會遇見哄尔,所以此模式是你學習時候必須掌握的假消。該在什么時候使用呢?LZ在學習中也常常問自己岭接,單例模式都有一個共性就是
1置谦、這個類沒有狀態(tài)
??有狀態(tài)的類:類里面有成員變量,而且成員變量是可變的亿傅、比如struts2的action媒峡、要求是多例的、因為他是有狀態(tài)的
??無狀態(tài)類:類里面沒有成員變量葵擎,或者有成員變量但是不可變的谅阿、或者成員變量是單例的、比如struts1的action、可以是單例的签餐。因為他是沒有狀態(tài)的
2寓涨、比如:工作上類似你一個AppServer類作為啟動資源類并進行各種初始化工作,那么你的這個類就可以寫出單例氯檐,因為這個類在使用的過程中并不需要每次都進行new來實例對象戒良,我們只需要單獨的一份就可以了。
說白了單例就是你new無數(shù)個其實都是一樣的冠摄,并且在邏輯上如果new多個也會發(fā)生邏輯錯誤糯崎。
在網(wǎng)上該模式又分為懶漢、餓漢等幾種形式河泳。LZ就不進行細分了沃呢,因為LZ并不想在學習單例模式的時候再對文字進行理解記憶。下面就是幾種逐漸進化的單例模式:
一拆挥、這種也是學習設(shè)計模式時老師講解的最初級版本
//這是在不考慮并發(fā)訪問的情況下標準的單例模式的構(gòu)造方式薄霜,這種方式通過幾個地方來限制了我們?nèi)〉降膶嵗俏ㄒ坏摹?public class Singleton {
private Singleton(){}
private static Singleton singleton ;
public static Singleton getInstance (){
if(singleton == null){
singleton = new Singleton();
}
return singleton ;
}
}
這種寫法是在初學是不考慮并發(fā)情況的構(gòu)造方式,通過幾點來確定獲取唯一的實例:
1纸兔、使用private權(quán)限的構(gòu)造器惰瓜,使得客戶端(使用者)不能夠隨意創(chuàng)建對象
2、使用static關(guān)鍵字來使得屬性所指向的對象在每一個類中都是唯一的
3汉矿、static方法鸵熟,使得客戶端可以直接通過類.方法名調(diào)用。如果沒有static就會使得客戶端無法獲取實例進行調(diào)用
??上面的方法屬于大學畢業(yè)階段的代碼负甸,因為他并沒有考慮到如果在多線程環(huán)境會造成的影響流强,如果多個線程(A\B)同時來訪問getInstance這個方法,那么在if判斷這里A線程判斷為空呻待,然而B線程正好在執(zhí)行singleton = new Singleton(); 創(chuàng)建實例方法打月,但是并沒有真正實例出對象,那么A線程也會繼續(xù)執(zhí)行singleton = new Singleton(); 創(chuàng)建實例方法蚕捉。從而導致會創(chuàng)建多個實例奏篙。
二、有人說第一種是沒考慮多線程那么就加鎖
/*此種加鎖方式會導致運行速度降低迫淹,當一個線程進行訪問的時候秘通,其余所有線程都將掛起等待
*/
public class StupidSynchronizedSingleton {
private StupidSynchronizedSingleton (){}
private static StupidSynchronizedSingleton badsingleton ;
public synchronized static StupidSynchronizedSingleton getInstance (){
if(badsingleton == null){
badsingleton = new StupidSynchronizedSingleton();
}
return badsingleton ;
}
}
由于該方法的做法實在是太愚蠢了,所以LZ給它取名Stupid敛熬。上面的做法是將getInstance ()進行同步來解決多線程問題肺稀,但是當訪問getInstance ()時,其余的線程會進行掛起应民,造成無謂的等待话原,顯然這種等待是沒有必要的夕吻。
三、在第二種方法上適當?shù)男薷木涂梢越鉀Q那種無謂的等待了繁仁。
//這種實現(xiàn)方式實現(xiàn)了雙重鎖機制
/**
* 首先要明白在JVM創(chuàng)建新的對象時涉馅,主要要經(jīng)過三步。
1.分配內(nèi)存
2.初始化構(gòu)造器
3.將對象指向分配的內(nèi)存的地址
但是在new實例的時候有可能是先將對象分配給內(nèi)存黄虱,在初始化稚矿。這個時候返回的synsingleton就會出現(xiàn)未知的錯誤
*
*/
public class SynchronizedSingleton {
private SynchronizedSingleton (){}
private static SynchronizedSingleton synsingleton ;
public static SynchronizedSingleton getInstance (){
if(synsingleton == null){//1
synchronized (SynchronizedSingleton.class) {
if(synsingleton == null){//2
synsingleton = new SynchronizedSingleton();
}
}
}
return synsingleton ;
}
}
上面方法相比較第二種方法做的同步就要正確得多了,并沒有在方法上直接進行同步捻浦,而是判斷了變量是否為null之后再進行同步晤揣,否則就直接進行返回,從而減少了在有實例情況下的等待時間默勾。
假設(shè)synsingleton為null(1)碉渡,此時有A/B線程同時執(zhí)行到synchronized塊聚谁,假設(shè)A線程搶占到資源再次執(zhí)行synsingleton為null(2)當判斷為true時母剥,就進行實例化對象,否則返回形导。B之后進入同步塊是同樣环疼。再次執(zhí)行判斷的原因是確保多個線程進入注釋1后代碼的邏輯正確性。從上面代碼中可以看到有兩次判斷是否為null朵耕,這就是所謂的雙重鎖機制炫隶。
上面代碼從表面上看是沒有任何問題的,但是如果你了解JVM創(chuàng)建對象的邏輯步驟你就會發(fā)現(xiàn)上面做法也會出現(xiàn)問題阎曹。具體造成問題的原因看代碼上面的注釋伪阶。所以為了避免我們在創(chuàng)建對象時發(fā)生的此種問題,我們最好是交給JVM進行处嫌。
四栅贴、將創(chuàng)建對象的時機將給JVM加載類的時候進行(類加載這里就不詳細說明了,有時間單獨寫一個我對類加載過程的理解的一篇文章)
/*屬性為static的會在類加載的時候初始化熏迹,所以在初始化進行一半的時候檐薯,別的線程 *是無法使用的,這個是jvm保證的
*/
public class InnerSingleton {
private InnerSingleton (){};
public static InnerSingleton getInstance (){
return SingletonInstance.singleton;
}
private static class SingletonInstance {
private static final InnerSingleton singleton = new InnerSingleton();
}
}
首先static的成員變量會在類加載的時候進行初始化注暗,所以singleton在代碼調(diào)用之前就已經(jīng)實例化好了坛缕。
五、枚舉量來實現(xiàn)單例模式:
1捆昏、 自由序列化赚楚;
2、 保證只有一個實例(即使使用反射機制也無法多次實例化一個枚舉量)骗卜;
3直晨、 線程安全搀军;
public class EnumSingleton {
private EnumSingleton() {}
private enum InstanceHolder {
INSTANCE;
private EnumSingleton value;
private InstanceHolder() {
value = new EnumSingleton();
}
}
public static EnumSingleton getInstance() {
return InstanceHolder.INSTANCE.value;
}
}
枚舉形式不僅能避免多線程同步問題,而且還能防止反序列化重新創(chuàng)建新的對象勇皇,但是枚舉是1.5版本之后的新特性罩句,所以這種方式很少用到
以上就是java單例模式的各種寫法,當然在實際運用中你可以使用四敛摘、五方法來創(chuàng)建门烂,如果是應(yīng)聘,面試官叫你寫單例模式你可以都寫出來然后講出各自的優(yōu)缺點兄淫,這樣相信你對單例模式的掌握已經(jīng)熟練了屯远。
上面就是LZ對單例模式的理解,感謝各位的收看捕虽。這篇文章也是LZ在簡書上寫的第一篇慨丐,后續(xù)也會繼續(xù)分享自己對Java知識的理解,當然LZ并不是什么大牛泄私,也是在不斷的學習過程中分享自己理解房揭,有什么問題可以在文章下發(fā)留言進行交流。有錯的地方LZ也會改正晌端。謝謝捅暴!