單例模式是指對(duì)一個(gè)對(duì)象進(jìn)行一次實(shí)例化,然后全局都可以調(diào)用該實(shí)例化對(duì)象來完成項(xiàng)目的開發(fā)。
在計(jì)算機(jī)系統(tǒng)中,線程池、緩存、日志對(duì)象、對(duì)話框、打印機(jī)、顯卡的驅(qū)動(dòng)程序?qū)ο蟪1辉O(shè)計(jì)成單例。這些應(yīng)用都或多或少具有資源管理器的功能。每臺(tái)計(jì)算機(jī)可以有若干個(gè)打印機(jī),但只能有一個(gè)Printer Spooler,以避免兩個(gè)打印作業(yè)同時(shí)輸出到打印機(jī)中。每臺(tái)計(jì)算機(jī)可以有若干通信端口,系統(tǒng)應(yīng)當(dāng)集中管理這些通信端口,以避免一個(gè)通信端口同時(shí)被兩個(gè)請(qǐng)求同時(shí)調(diào)用??傊?,選擇單例模式就是為了避免不一致狀態(tài),避免政出多頭。
實(shí)現(xiàn)單例的不同方式
餓漢式單例
餓漢式單例是指在方法調(diào)用前,實(shí)例就已經(jīng)創(chuàng)建好了。下面是實(shí)現(xiàn)代碼:
package com.thread.singleton;
/**
* 單例模式-- 餓漢式
* Created by Fant.J.
* 2018/2/25 19:24
*/
public class Singleton1 {
/** 私有化構(gòu)造方法,在外部不能實(shí)例化對(duì)象 */
private Singleton1(){}
/** 在這里實(shí)例化 靜態(tài)對(duì)象 (優(yōu)點(diǎn):不存在線程安全問題。 缺點(diǎn):每次調(diào)用都實(shí)例化,占用空間) */
private static Singleton1 singleton1 = new Singleton1();
public static Singleton1 getInstance(){
return singleton1;
}
}
優(yōu)點(diǎn):不存在線程安全問題。 缺點(diǎn):每次調(diào)用都實(shí)例化,占用空間
懶漢式單例
懶漢式單例是指在方法調(diào)用獲取實(shí)例時(shí)才創(chuàng)建實(shí)例,因?yàn)橄鄬?duì)餓漢式顯得“不急迫”,所以被叫做“懶漢模式”。下面是實(shí)現(xiàn)代碼:
package com.thread.singleton;
/**
* 單例模式 -- 懶漢式
* Created by Fant.J.
* 2018/2/25 19:30
*/
public class Singleton2 {
private Singleton2(){}
private static Singleton2 instance;
public synchronized static Singleton2 getInstance() {
/* 下面這段代碼 不是原子性操作 會(huì)出現(xiàn)線程安全問題 。**/
if (instance == null) {
instance = new Singleton2();
}
return instance;
}
}
在這段代碼中,在if語句里面,就可能跑有多個(gè)線程同步判斷和同步new。會(huì)產(chǎn)生線程安全問題。
解決方法:
- 給方法加上synchronized(變成單線程,影響性能)
- 給代碼塊加synchronized(雙重檢查加鎖)
雖然2方法解決了性能問題, 但是還會(huì)有問題 。
問題來自 jvm 的優(yōu)化:指令重排序(有興趣了解)
我們可以在對(duì)象中添加volatile 關(guān)鍵字來 不讓jvm對(duì)該 對(duì)象做優(yōu)化
完善后的代碼如下:
package com.thread.singleton;
/**
* 單例模式 -- 懶漢式
* Created by Fant.J.
* 2018/2/25 19:30
*/
public class Singleton2 {
private Singleton2(){}
private static volatile Singleton2 instance;
public synchronized static Singleton2 getInstance() {
/* 下面這段代碼 不是原子性操作 會(huì)出現(xiàn)線程安全問題 。
解決方法:1.給方法加上synchronized(變成單線程,影響性能)
2.給代碼塊加synchronized(雙重檢查加鎖)
雖然2方法解決了性能問題, 但是還會(huì)有問題 。
問題來自 jvm 的優(yōu)化:指令重排序(有興趣了解)
我們可以在對(duì)象中添加volatile 關(guān)鍵字來 不讓jvm對(duì)該 對(duì)象做優(yōu)化
**/
if (instance == null) {
synchronized (Singleton2.class){
if (instance == null){
instance = new Singleton2();
}
}
}
return instance;
}
}