單例模式應(yīng)該是Android開(kāi)發(fā)中常用的一種設(shè)計(jì)模式。不僅我們經(jīng)常用到,Android源碼中也經(jīng)??梢钥吹絾卫J降倪\(yùn)用。
單例模式很簡(jiǎn)單,但我相信有的同學(xué)并沒(méi)有完全搞清楚,平時(shí)寫單例模式也就隨便找一個(gè)模板就開(kāi)始用。
廢話不多說(shuō),下面正式開(kāi)始。
單例模式分為:
餓漢
懶漢
懶漢線程安全
DCL
靜態(tài)內(nèi)部類
餓漢
public class TestMode {
private static final TestMode testMode = new TestMode();
private TestMode(){
System.out.println("TestMode 初始化完成");
}
public static TestMode getInstance(){
return testMode;
}
public?static void doSomething(){
System.out.println("doSomething");
}
}
我們?cè)趍ain函數(shù)中調(diào)用doSomething方法,運(yùn)行結(jié)果為:

上面是餓漢式的單例模式,我們可以看到類的對(duì)象在還沒(méi)有使用getInstance方法的時(shí)候就已經(jīng)初始化完成了,這樣就會(huì)對(duì)內(nèi)存造成不必要的消耗。
懶漢
public class TestMode {
private static TestMode testMode;
private TestMode() {
System.out.println("TestMode 初始化完成");
}
public static TestMode getInstance() {
if (testMode ==null) {
testMode = new TestMode();
}
return testMode;
}
public static void doSomething() {
System.out.println("doSomething");
}
}
我們?cè)趍ain函數(shù)中調(diào)用doSomething方法,運(yùn)行結(jié)果為:

以上是懶漢式,我們可以看到調(diào)用類中的其他方法并不會(huì)創(chuàng)建實(shí)例,而是在調(diào)用getInstance方法之后才會(huì)創(chuàng)建實(shí)例。但是這里會(huì)出現(xiàn)一個(gè)問(wèn)題,當(dāng)我們?cè)诙嗑€程中調(diào)用getInstance方法的時(shí)候會(huì)存在線程不安全的情況。
例:

我們用以上的測(cè)試方法,得到的測(cè)試結(jié)果為:

我們可以看到,這里的測(cè)試結(jié)果中我們有兩次初始化了TestMode。所以懶漢式有線程不安全的問(wèn)題。那么我們?nèi)绾谓鉀Q這個(gè)問(wèn)題呢,請(qǐng)大家往下面看。
懶漢線程安全
public class TestMode {
private static?volatile TestMode testMode;
private TestMode() {
System.out.println("TestMode 初始化完成");
}
public static synchronized TestMode getInstance() {
if (testMode ==null) {
testMode = new TestMode();
}
return testMode;
}
public static TestMode getInstance2() {
synchronized (TestMode.class) {
if (testMode ==null) {
testMode = new TestMode();
}
}
return testMode;
}
public static void doSomething() {
System.out.println("doSomething");
}
}
在代碼中加入了同步鎖和volatile關(guān)鍵字,這樣就保證了線程安全。上面的代碼中用了兩種方法來(lái)加入同步鎖,第一種是在方法上加入,第二種是在方法里面加入,這兩種都是可以的。我也用相同的測(cè)試方法測(cè)試多次,并沒(méi)有出現(xiàn)線程安全問(wèn)題。
DCL
上面的懶漢線程安全式因?yàn)榧尤肓送芥i,每次調(diào)用方法都會(huì)進(jìn)行上鎖,所以性能上會(huì)有一定的影響,這也是它的缺點(diǎn)。接下來(lái)我就給大家介紹一種更好的單例模式寫法,那就是DCL。
public class TestMode {
private static volatile TestMode testMode;
private TestMode() {
System.out.println("TestMode 初始化完成");
}
public static TestMode getInstance() {
if (testMode ==null) {
synchronized (TestMode.class) {
if (testMode ==null) {
testMode = new TestMode();
}
}
}
return testMode;
}
public static void doSomething() {
System.out.println("doSomething");
}
}
上面的代碼就是DCL,這個(gè)方式解決了線程安全問(wèn)題,同時(shí)也解決了性能問(wèn)題。
靜態(tài)內(nèi)部類
最后我再給大家介紹一種靜態(tài)內(nèi)部類的寫法,這種寫法代碼更加簡(jiǎn)潔,也不存在線程安全以及性能問(wèn)題。
public class TestMode {
private TestMode() {
System.out.println("TestMode 初始化完成");
}
public static TestMode getInstance() {
return TestModeHolder.testMode;
}
public static void doSomething() {
System.out.println("doSomething");
}
private static class TestModeHolder {
private static final TestMode testMode = new TestMode();
}
}
這個(gè)靜態(tài)內(nèi)部類的寫法其實(shí)是在餓漢式的基礎(chǔ)上進(jìn)行了改動(dòng),利用了類的特性,這樣在使用TestMode類中的其他方法的時(shí)候并不會(huì)初始化TestModeHolder類中對(duì)象。推薦大家以后使用這個(gè)寫法。
其實(shí)單例模式還有枚舉的寫法,只是現(xiàn)在Android不建議使用枚舉,所以我這里就不介紹了。文章很簡(jiǎn)單,如果有錯(cuò)誤的地方希望大家提出來(lái),我會(huì)對(duì)文章進(jìn)行改進(jìn)。馬上過(guò)完年又要開(kāi)始上班了,希望小伙伴們加油,早日用代碼改變世界?。?!