單例模式(Singleton Pattern)是 Java 中最簡單的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式吧秕,它提供了一種創(chuàng)建對(duì)象的最佳方式。
這種模式涉及到一個(gè)單一的類迹炼,該類負(fù)責(zé)創(chuàng)建自己的對(duì)象砸彬,同時(shí)確保只有單個(gè)對(duì)象被創(chuàng)建颠毙。這個(gè)類提供了一種訪問其唯一的對(duì)象的方式,可以直接訪問砂碉,不需要實(shí)例化該類的對(duì)象蛀蜜。
注意:
- 1、單例類只能有一個(gè)實(shí)例增蹭。
- 2滴某、單例類必須自己創(chuàng)建自己的唯一實(shí)例。
- 3滋迈、單例類必須給所有其他對(duì)象提供這一實(shí)例霎奢。
單例模式它有以下幾個(gè)要素:
- 1.私有的構(gòu)造方法。
- 2.指向自己實(shí)例的私有靜態(tài)引用饼灿。
- 3.以自己實(shí)例為返回值的靜態(tài)的公有的方法幕侠。
餓漢式單例模式
public class Hungry {
private final static Hungry hungry = new Hungry();
private Hungry() {}
public static Hungry getHungry() {
return hungry;
}
}
餓漢式單例模式的好處:
它是在類加載的時(shí)候就立即初始化,并且創(chuàng)建單例對(duì)象赔退,沒有加任何的鎖橙依、絕對(duì)線程安全证舟,在線程還沒出現(xiàn)以前就是實(shí)例化了硕旗,不可能存在訪問安全問題,執(zhí)行效率比較高女责,在用戶體驗(yàn)上來說漆枚,比懶漢式更好。
餓漢式單例模式的缺點(diǎn):
類加載的時(shí)候就初始化抵知,不管你用還是不用墙基,都占著空間浪費(fèi)了內(nèi)存,有可能占著茅坑不拉屎刷喜。
懶漢式單例模式
public class SafeDoubleCheckedLocking {
//volatile 防止指令重排序 內(nèi)存可見性残制、
private static volatile SafeDoubleCheckedLocking safeDoubleCheckedLocking = null;
private SafeDoubleCheckedLocking() {}
public static SafeDoubleCheckedLocking getInstance() {
if(safeDoubleCheckedLocking == null) {
synchronized (SafeDoubleCheckedLocking.class) {
// 第一個(gè)線程已經(jīng)創(chuàng)建了,第二個(gè)線程無需在創(chuàng)建 故此第二重判斷
if(safeDoubleCheckedLocking == null) {
//這里有指令重排序的可能
/**
* memory = allocate(); //1:分配對(duì)象的內(nèi)存空間
* ctorInstance(memory); //2:初始化對(duì)象
* instance = memory; //3:設(shè)置instance指向剛分配的內(nèi)存地址
* <p>
* memory = allocate(); //1:分配對(duì)象的內(nèi)存空間
* instance = memory; //3:設(shè)置instance指向剛分配的內(nèi)存地址
* 注意掖疮,此時(shí)對(duì)象還沒有被初始化初茶!
* ctorInstance(memory); //2:初始化對(duì)象
*/
safeDoubleCheckedLocking = new SafeDoubleCheckedLocking();
}
}
}
return safeDoubleCheckedLocking;
}
}
懶漢式單例模式的好處:
在使用該類的時(shí)候在初始化對(duì)象,不會(huì)對(duì)內(nèi)存造成浪費(fèi)浊闪。
懶漢式單例模式的缺點(diǎn):
加synchronized同步鎖的恼布,性能就會(huì)受到影響。
注:雙重檢驗(yàn)鎖是面試經(jīng)常提問到的事搁宾,大家切記要搞懂折汞。
懶漢式單例內(nèi)部類模式(算是懶漢式單例模式改進(jìn)版)
public class LazyPromote {
// private static boolean initialized = false;
//默認(rèn)使用LazyThree的時(shí)候,會(huì)先初始化內(nèi)部類
//如果沒使用的話盖腿,內(nèi)部類是不加載的
private LazyThree() {
/*synchronized (LazyThree.class) {
if (initialized == false) {
initialized = !initialized;
} else {
throw new RuntimeException("單例已被侵犯");
}
} */
}
//每一個(gè)關(guān)鍵字都不是多余的
//static 是為了使單例的空間共享
//final 保證這個(gè)方法不會(huì)被重寫爽待,重載
public static final LazyPromote getInstance() {
//在返回結(jié)果以前,一定會(huì)先加載內(nèi)部類
return LazyHolder.LAZY;
}
//默認(rèn)不加載
private static class LazyHolder {
private static final LazyThree LAZY = new LazyPromote();
}
}
懶漢式單例內(nèi)部類模式改進(jìn)版
特點(diǎn):在外部類被調(diào)用的時(shí)候內(nèi)部類才會(huì)被加載,內(nèi)部類一定是要在方法調(diào)用之前初始化,巧妙地避免了線程安全問題這種形式兼顧餓漢式的內(nèi)存浪費(fèi),也兼顧synchronized性能問題,完美地屏蔽了這兩個(gè)缺點(diǎn),史上最牛B的單例模式的實(shí)現(xiàn)方式.
注冊(cè)登記式單例
public class BeanFactoryNew {
private BeanFactoryNew() {
}
//線程安全
private static Map<String, Object> ioc = new ConcurrentHashMap();
public static synchronized <T> T getBean(Class<T> className) {
if (!ioc.containsKey(className.getName())) {
T obj = null;
try {
obj =className.newInstance();
ioc.put(className.getName(), obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
} else {
return (T)ioc.get(className.getName());
}
}
}