十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊
量身定制 + 運營維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
目錄
前言:
單線程下的單例模式
餓漢模式
懶漢模式
多線程下的單例模式
懶漢模式的修改
1.加鎖
2.有條件的加鎖
3.解決內(nèi)存可見性和指令重排序
本片文章介紹設(shè)計模式中的一個模式——單例模式。
單例模式就是只允許創(chuàng)建出一個實例的類。比如之前在使用JDBC編程的時候的DataSource這個類,就可以使用單例模式。見這篇文章http://t.csdn.cn/uq1lR。
其中單例模式有很多種實現(xiàn)方法,這里只用懶漢模式和餓漢模式來實現(xiàn)單例模式。
單線程中,沒有線程安全問題,其代碼會簡單很多。
餓漢模式餓漢模式體現(xiàn)出一個字——急。因為這個實例直接就是在類加載階段就被創(chuàng)建出來。
class SingletonHungry{
// 用static修飾,這樣在類加載階段就有了這個實例
private static SingletonHungry instance = new SingletonHungry();
// 用靜態(tài)公開方法返回實例
public static SingletonHungry getInstance() {
return instance;
}
// 為了防止實例化多個對象,把構(gòu)造方法用private修飾
private SingletonHungry(){};
}
public class SingletonTest1 {
public static void main(String[] args) {
SingletonHungry instance1 = SingletonHungry.getInstance();
SingletonHungry instance2 = SingletonHungry.getInstance();
System.out.println(instance1 == instance2);
//SingletonHungry instance3 = new SingletonHungry();
}
}
懶漢模式突出一個字——懶。這個實例是只有調(diào)用獲取實例方法的時候才創(chuàng)建出來。
class SingletonLazy{
private static SingletonLazy instance = null;
// 只有在調(diào)用了該方法后才創(chuàng)造出了實例
public static SingletonLazy getInstance() {
// 如果已經(jīng)創(chuàng)建了該實例,就直接返回之前的實例
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
// 為了防止實例化多個對象,把構(gòu)造方法用private修飾
private SingletonLazy(){}
}
public class SingletonTest2 {
public static void main(String[] args) {
SingletonLazy instance1 = SingletonLazy.getInstance();
SingletonLazy instance2 = SingletonLazy.getInstance();
System.out.println(instance1 == instance2);
//SingletonLazy instance3 = new SingletonLazy();
}
}
代碼結(jié)果如上。?
在上述代碼中,如果考慮多線程的話
餓漢模式?jīng)]有線程安全問題,它直接就是在類加載是創(chuàng)建出了一個實例,使用該實例也沒有其他修改的操作,直接返回即可。
懶漢模式是有線程安全問題。它又要判斷比較實例是否為null,又要創(chuàng)建一個實例,最后才返回實例。其中對于實例是由修改的操作的。只要有修改操作,就可能會有線程安全問題。如下圖:
對于這種又有讀,又有寫的操作,保持原子性——加鎖即可。
public SingletonThread getInstance() {
// 這里對于load cmp new這幾步加鎖,保持了其原子性
synchronized (SingletonThread.class) {
if (instance == null) {
instance = new SingletonThread();
}
}
return instance;
}
2.有條件的加鎖但是加鎖是一種開銷比較大的操作,上述加鎖操作并不是每次都要加鎖的。如果已經(jīng)創(chuàng)建了實例,直接返回實例即可。
public SingletonThread getInstance() {
// 如果實例未被創(chuàng)建,用加鎖的方法創(chuàng)建實例
// 如果創(chuàng)建,直接返回
if (instance == null) {
// 這里對于load cmp new這幾步加鎖,保持了其原子性
synchronized (SingletonThread.class) {
if (instance == null) {
instance = new SingletonThread();
}
}
}
return instance;
}
3.解決內(nèi)存可見性和指令重排序內(nèi)存可見性:因為instance要讀取并修改,所以對于內(nèi)存可見性的問題也要預(yù)防。
指令重排序:
上面的new實例的指令又分為三個順序步驟:
①申請內(nèi)存空間
②調(diào)用構(gòu)造方法,實例化一個對象
③把內(nèi)存空間的地址賦值給這個對象
這幾步可能可能會變成①③②。單線程下沒有問題,但是多線程就會有問題了。
要想解決這兩個問題,使用volatile關(guān)鍵字修飾instance即可。
private volatile static SingletonThread instance = null;
完整的懶漢模式的線程安全代碼如下:
// 修改懶漢模式,使其線程安全
class SingletonThread {
// 使用volatile解決內(nèi)存可見性和指令重排序問題
private volatile static SingletonThread instance = null;
public SingletonThread getInstance() {
// 如果實例未被創(chuàng)建,用加鎖的方法創(chuàng)建實例
// 如果創(chuàng)建,直接返回
if (instance == null) {
// 這里對于load cmp new這幾步加鎖,保持了其原子性
synchronized (SingletonThread.class) {
if (instance == null) {
instance = new SingletonThread();
}
}
}
return instance;
}
}
有什么問題評論區(qū)指出。希望可以幫到你。
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧