懶漢式單例:
注意點(diǎn): 類內(nèi)部私有封裝一個(gè)自己的引用構(gòu)造器私有化提供獲取該類內(nèi)部私有封裝的唯一方法缺點(diǎn):
懶漢式單例設(shè)計(jì)的實(shí)現(xiàn)沒有考慮過線程安全問題,它是非線程安全的,在并發(fā)環(huán)境下,很可能會(huì)出現(xiàn)多個(gè)SingleTon實(shí)例。
實(shí)例:
非線程安全:
public class Person { //類內(nèi)部封裝自己的引用,該引用必須私有 PRivate static Person person = null; //構(gòu)造器私有化 private Person(){ } //提供獲取單例的唯一接口 public static Person getInstance() { if(person == null) { person = new Person(); } return person; }}改造:
在獲取方法中,加入同步機(jī)制:
synchronized修飾獲取方法:
缺陷:
雖然線程安全了,但是每次都要進(jìn)行同步,因此會(huì)影響性能
//提供獲取單例的唯一接口public synchronized static Person getInstance() { if(person == null) { person =new Person(); } return person;}雙重檢查機(jī)制:
改善:
做了兩次判空操作,確保了只有第一次調(diào)用單例的時(shí)候才會(huì)做同步,也避免了同步的性能損耗
//提供獲取單例的唯一接口public static Person getInstance() { if(person ==null) { synchronized (Person.class) { if(person == null) { person =new Person(); } } } return person;}使用靜態(tài)內(nèi)部類并且加上final修飾的機(jī)制:
改善:
利用ClassLoader的機(jī)制類保證初始化單例對(duì)象的時(shí)候,只有一個(gè)線程,所以也是線程安全的,同時(shí)還沒有性能的損耗。
public class Person{ //構(gòu)造器私有化 private Person() { } //寫一個(gè)靜態(tài)內(nèi)部類,用來提供單例對(duì)象 private static class LazyHolder { public static final Person SINGLEINSTANCE = new Person(); } //獲取單例對(duì)象的方法 public static Person getInstance(){ return LazyHolder.SINGLEINSTANCE; }}餓漢式單例:
因?yàn)轲I漢式單例是在類創(chuàng)建的同時(shí),就已經(jīng)創(chuàng)建好了一個(gè)靜態(tài)的對(duì)象供給系統(tǒng)使用,以后不再改變,所以是天生線程安全的實(shí)例:
public class Person { //類內(nèi)部封裝自己的引用,該引用必須私有 private static Person person = new Person(); //構(gòu)造器私有化 private Person(){ } //提供獲取單例的唯一接口 public static Person getInstance() { return person; }}餓漢式和懶漢式的區(qū)別:
從名字上區(qū)分: 餓漢: 類一旦加載,就把單例初始化完成,保證取單例的時(shí)候,單例是絕對(duì)存在的懶漢: 比較懶,只有當(dāng)取單例的時(shí)候,才會(huì)去初始化這個(gè)單例對(duì)象線程安全: 餓漢式: 天生線程安全,可以直接不用擔(dān)心多線程的安全問題懶漢式: 本身是非線程安全的,為了實(shí)現(xiàn)線程安全,需要額外做操作。枚舉單例:
前面介紹了懶漢式單例、餓漢式單例,最近在網(wǎng)上看到有大神提出可以使用枚舉類型創(chuàng)建單例。優(yōu)點(diǎn): 我們知道,上述的這些不管是懶漢式、餓漢式,都逃不開一個(gè)問題:反射機(jī)制能夠進(jìn)行攻擊,這樣單例就失效了。因此如果想要對(duì)單例進(jìn)行保護(hù),就要使用枚舉單例了。枚舉類型天生就是線程安全的,也不需要去考慮線程安全問題。所以,看來看去還是枚舉單例用起來比較高大上。且看下面代碼實(shí)例:
public enum Person {INSTANCE;Person() { //單例構(gòu)造,默認(rèn)私有}@Overridepublic String toString() { return super.toString();}}class test{public void go(){ //直接使用枚舉類型調(diào)用單例 Person.INSTANCE.toString();}}新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注