這篇文章主要為大家展示了“java中單例模式有幾種寫法”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“java中單例模式有幾種寫法”這篇文章吧。
目前創(chuàng)新互聯(lián)已為千余家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)絡(luò)空間、綿陽服務(wù)器托管、企業(yè)網(wǎng)站設(shè)計、庫倫網(wǎng)站維護等服務(wù),公司將堅持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。
1、餓漢式
餓漢式是單例模式設(shè)計中比較經(jīng)典的實現(xiàn)方式。實現(xiàn)代碼如下:
//final不允許被繼承 public final class SingleTonEhangshi { //實例變量 private byte[] data = new byte[1024]; //在定義實例對象時直接初始化 private static SingleTonEhangshi instance = new SingleTonEhangshi(); //私有化構(gòu)造函數(shù),不允許外部NEW private SingleTonEhangshi() { } public static SingleTonEhangshi getInstance() { return instance; } }
餓漢式的實現(xiàn)關(guān)鍵在于instance作為類變量直接得到了初始化,如果我們主動使用SingleToEhangshi類,那么instance實例將會直接完成創(chuàng)建,包括其中的實例變量也都會得到初始化。
instance作為類變量,在類初始化的過程中會被收集進
總體說來,如果一個類中的成員變量比較少,且占用的內(nèi)存資源也不多,用餓漢式的方式實現(xiàn)單例模式也未嘗不可,只是其無法進行懶加載。
2、懶漢式
所謂懶漢式就是在使用類實例的時候再去創(chuàng)建,也就是說用到的時候我再創(chuàng)建,這樣就可以避免類在初始化的時候提前創(chuàng)建過早地占用內(nèi)存空間。實現(xiàn)代碼如下:
//final不允許被繼承 public final class SingleTonLhangshi { //實例變量 private byte[] data = new byte[1024]; //定義實例,但是不直接初始化 private static SingleTonLhangshi instance = null; //私有化構(gòu)造函數(shù),不允許外部NEW private SingleTonLhangshi() { } public static SingleTonLhangshi getInstance() { if (null == instance) { instance = new SingleTonLhangshi(); } return instance; } }
類變量instance=null,因此當類被初始化的時候instance并不會立刻被實例化,而是在getInstance()方法被調(diào)用時判斷instance實例是否被實例化,如果沒有實例化在調(diào)用私有構(gòu)造方法進行實例化操作。
懶漢式寫法在多線程環(huán)境下,會存在同一時間多個線程同時看到null==instance的情況,從而導(dǎo)致instance會被實例化多次,從而無法保證單例的唯一性。
3、懶漢式+同步方法
懶漢式的單例實現(xiàn)方式可以保證實例的懶加載,但是卻無法保證實例的唯一性。在多線程環(huán)境下由于instance為共享數(shù)據(jù),當多個線程訪問使用時,需要保證數(shù)據(jù)的同步性,所以如果需要保證懶漢式實例的唯一性,我們可以通過同步的方式來實現(xiàn)。代碼如下:
/final不允許被繼承 public final class SingleTonLhangshiSync { //實例變量 private byte[] data = new byte[1024]; //定義實例,但是不直接初始化 private static SingleTonLhangshiSync instance = null; //私有化構(gòu)造函數(shù),不允許外部NEW private SingleTonLhangshiSync() { } //向getInstance方法加入同步控制,每次只能有一個線程能夠進入 public static synchronized SingleTonLhangshiSync getInstance() { if (null == instance) { instance = new SingleTonLhangshiSync(); } return instance; } }
采用懶漢式+數(shù)據(jù)同步的方法既滿足了懶加載又能夠100%保證instance實例的唯一性。但是,synchronized關(guān)鍵字的排它性會導(dǎo)致getInstance()方法同一時刻只能被一個線程訪問,性能會比較低下。
4、Double-Check
Double-Check是一種比較聰明的設(shè)計方式,它提供了一種高效的數(shù)據(jù)同步策略,那就是首次初始化的時候加鎖,之后則允許多個線程同時進行g(shù)etInstance()方法的調(diào)用來獲得類的實例。代碼如下:
//final不允許被繼承 public final class SingletonDoubleCheck { //實例變量 private byte[] data = new byte[1024]; //定義實例,但是不直接初始化 private static SingletonDoubleCheck instance = null; Connection con; Socket socket; //私有化構(gòu)造函數(shù),不允許外部NEW private SingletonDoubleCheck() { this.con = con;//初始化 this.socket = socket;//初始化 } public static SingletonDoubleCheck getInstance() { //當instance為null時,進入同步代碼塊,同時該判斷避免了每次都需要進入同步代碼塊,可以提高效率 if (null == instance) { //只有一個線程能夠獲得SingletonDoubleCheck.class關(guān)聯(lián)的monitor synchronized (SingletonDoubleCheck.class) { //判斷如果instance為null則創(chuàng)建 if (null == instance) { instance = new SingletonDoubleCheck(); } } } return instance; } }
當兩個線程發(fā)現(xiàn)null==instance成立時,只有一個線程有資格進入同步代碼塊,完成對instance的初始化,隨后的線程發(fā)現(xiàn)null==instance不成立則無須進行任何操作,以后對getInstance的訪問就不會再需要進行數(shù)據(jù)同步了。
此種方式看起來是既滿足了懶加載,又保證了instance實例的唯一性,并且還提供了比較高效的數(shù)據(jù)同步策略,可以允許多個線程同時對getInstance進行訪問。但是這種方式在多線程的情況下,可能會引起空指針異常,這是因為如果在如上代碼的構(gòu)造方法中還存在初始化其他資源的情況的話,由于JVM運行時存在指令重排的情況,這些資源在實例化時順序并無前后關(guān)系的約束,那么在這種情況下,就極有可能是instance最先被實例化,而con和socket并未完成實例化,而未完成實例化的實例在調(diào)用其方法時將會拋出空指針異常。
5、Volatile+Double-Check
為了解決Double-Check指令重排導(dǎo)致的空指針問題,可以用volatile關(guān)鍵字防止這種重排序的發(fā)生。因此代碼只需要稍作修改就能滿足多線程下的單例、懶加載以及實例的高效性了。代碼如下:
//final不允許被繼承 public final class SingletonDoubleCheck { //實例變量 private byte[] data = new byte[1024]; //定義實例,但是不直接初始化 private static volatile SingletonDoubleCheck instance = null; Connection con; Socket socket; //私有化構(gòu)造函數(shù),不允許外部NEW private SingletonDoubleCheck() { this.con = con;//初始化 this.socket = socket;//初始化 } public static SingletonDoubleCheck getInstance() { //當instance為null時,進入同步代碼塊,同時該判斷避免了每次都需要進入同步代碼塊,可以提高效率 if (null == instance) { //只有一個線程能夠獲得SingletonDoubleCheck.class關(guān)聯(lián)的monitor synchronized (SingletonDoubleCheck.class) { //判斷如果instance為null則創(chuàng)建 if (null == instance) { instance = new SingletonDoubleCheck(); } } } return instance; } }
6、Holder方式
Holder方式完全借助了類加載的特點。代碼如下:
//不允許被繼承 public final class SingletonHolder { //實例變量 private byte[] data = new byte[1024]; private SingletonHolder() { } //在靜態(tài)內(nèi)部類中持有單例類的實例,并且可直接被初始化 private static class Holder { private static SingletonHolder instance = new SingletonHolder(); } //調(diào)用getInstance方法,事實上是獲得Holder的instance靜態(tài)屬性 public static SingletonHolder getInstance() { return Holder.instance; } }
在單例類中并沒有instance的靜態(tài)成員,而是將其放到了靜態(tài)內(nèi)部類Holder之中,因此單例類在初始化的過程中并不會創(chuàng)建SingletonHolder的實例,內(nèi)部類Holder中定義了SingletonHolder的靜態(tài)變量,并且直接進行了實例化,只有當Holder被主動引用的時候才會創(chuàng)建SingletonHolder的實例。
SingletonHolder實例的創(chuàng)建過程在Java程序編譯時期收集至
7、枚舉方式
枚舉方式在很多開源框架中也應(yīng)用得比較廣泛,枚舉類型不允許被繼承,同樣是線程安全的,并且只能被實例化一次,但是枚舉類型不能夠?qū)崿F(xiàn)懶加載。用枚舉類型,實現(xiàn)單例模式的代碼如下:
public class SingletonEnum { //實例變量 private byte[] data = new byte[1024]; private SingletonEnum() { } //使用枚舉充當Holder private enum EnumHolder { INSTANCE; private SingletonEnum instance; EnumHolder() { this.instance = new SingletonEnum(); } private SingletonEnum getInstance() { return instance; } } public static SingletonEnum getInstance() { return EnumHolder.INSTANCE.getInstance(); } }
以上是“java中單例模式有幾種寫法”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!