1.單例模式的作用
為什么需要單例?
- 節(jié)省內(nèi)存和計(jì)算
- 保證結(jié)果正確
- 方便管理
2.單例模式的適用場景
- 無狀態(tài)的工具類:比如日志工具類,不管是在哪里使用,我們需要的只是它幫我們記錄日志信息,除此之外,并不需要在它的實(shí)例對(duì)象上存儲(chǔ)任何狀態(tài),這時(shí)候我們就只需要一個(gè)實(shí)例對(duì)象即可。
- 全局信息類:比如我們?cè)谝粋€(gè)類上記錄網(wǎng)站的訪問次數(shù),我們不希望有的訪問被記錄在對(duì)象 A 上,有的卻記錄在對(duì)象 B 上,這時(shí)候我們就讓這個(gè)類成為單例。
3.餓漢式
靜態(tài)常量(可用)
/**
* 餓漢式(靜態(tài)常量)(可用)
*/
public class Singleton1 {
// 由于加了static關(guān)鍵字,根據(jù)JVM的規(guī)定,在類加載的時(shí)候就會(huì)完成INSTANCE的實(shí)例化,這樣就避免了線程同步問題
private final static Singleton1 INSTANCE = new Singleton1();
// 構(gòu)造函數(shù)是私有的
private Singleton1() {
}
public static Singleton1 getInstance() {
return INSTANCE;
}
}
靜態(tài)代碼塊(可用)
/**
* 餓漢式(靜態(tài)代碼塊)(可用)
*/
public class Singleton2 {
private final static Singleton2 INSTANCE;
// 與上一種寫法類似,由JVM保證了線程安全
static {
INSTANCE = new Singleton2();
}
// 構(gòu)造函數(shù)是私有的
private Singleton2() {
}
public static Singleton2 getInstance() {
return INSTANCE;
}
}
4.懶漢式
線程不安全(不可用)
/**
* 懶漢式(線程不安全)(不可用)
*/
public class Singleton3 {
private static Singleton3 instance;
// 構(gòu)造函數(shù)是私有的
private Singleton3() {
}
public static Singleton3 getInstance() {
// 這種寫法是線程不安全的,不可用
if (instance == null) {
instance = new Singleton3();
}
return instance;
}
}
同步方法(線程安全,但不推薦用)
/**
* 懶漢式(線程安全)(不推薦用)
*/
public class Singleton4 {
private static Singleton4 instance;
// 構(gòu)造函數(shù)是私有的
private Singleton4() {
}
// 這種寫法雖然是線程安全的,但是效率太低,不推薦用
public synchronized static Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
}
同步代碼塊(線程不安全,不可用)
/**
* 懶漢式(線程不安全)(不可用)
*/
public class Singleton5 {
private static Singleton5 instance;
// 構(gòu)造函數(shù)是私有的
private Singleton5() {
}
public static Singleton5 getInstance() {
// 這種寫法并不是線程安全的,不可用
if (instance == null) {
synchronized (Singleton5.class) {
instance = new Singleton5();
}
}
return instance;
}
}
雙重檢查 + volatile(推薦用)
優(yōu)點(diǎn):線程安全,延遲加載,效率較高。
/**
* 雙重檢查 + volatile(推薦用)
*/
public class Singleton6 {
// volatile防止重排序
private volatile static Singleton6 instance;
// 構(gòu)造函數(shù)是私有的
private Singleton6() {
}
public static Singleton6 getInstance() {
// 雙重檢查保證線程安全
if (instance == null) {
synchronized (Singleton6.class) {
if (instance == null) {
instance = new Singleton6();
}
}
}
return instance;
}
}
為什么要用 volatile?
新建對(duì)象 rs = new Resource()
實(shí)際上有 3 個(gè)步驟:
- construct empty resource()
- call constructor
- assign to rs
如下圖所示,重排序會(huì)帶來NPE問題(NullPointerException, 空指針異常),而使用 volatile 可以防止重排序。
靜態(tài)內(nèi)部類(推薦用)
/**
* 靜態(tài)內(nèi)部類(線程安全,懶加載)(推薦用)
*/
public class Singleton7 {
// 構(gòu)造函數(shù)是私有的
private Singleton7() {
}
// 由JVM的規(guī)定可知,這種寫法同時(shí)滿足了線程安全和懶加載兩個(gè)優(yōu)點(diǎn)
private static class SingletonInstance {
private static final Singleton7 INSTANCE = new Singleton7();
}
public static Singleton7 getInstance() {
return SingletonInstance.INSTANCE;
}
}
枚舉(推薦用)
單例模式的書寫:
/**
* 枚舉(線程安全,懶加載)(推薦用)
*/
public enum Singleton8 {
INSTANCE;
public void whatever() {
}
}
單例的使用:
Singleton8.INSTANCE.whatever();
哪種單例的實(shí)現(xiàn)方案最好?文章來源:http://www.zghlxwxcb.cn/news/detail-799856.html
Joshua Bloch 大神在《Effective Java》中明確表達(dá)過的觀點(diǎn):使用枚舉實(shí)現(xiàn)單例的方法雖然還沒有廣泛采用,但是單元素的枚舉類型已經(jīng)成為實(shí)現(xiàn) Singleton 的最佳方法。文章來源地址http://www.zghlxwxcb.cn/news/detail-799856.html
- 寫法簡單
- 線程安全有保障
- 懶加載
- 避免反序列化破壞單例
到了這里,關(guān)于單例模式的八種寫法、單例和并發(fā)的關(guān)系的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!