江湖傳言里的設(shè)計模式-單例設(shè)計模式
簡介:什么是單例設(shè)計模式和應(yīng)用
備注:面試重點考查
-
單例設(shè)計模式:
- 這個是最簡單的設(shè)計模式,所以拎出來第一個講, 但事實卻不是轰坊。
- 單例意思只包含一個對象被稱為單例的特殊類
- 通過單例模式可以保證系統(tǒng)中铸董,應(yīng)用該模式的類只有一個對象實例
-
使用場景
- 業(yè)務(wù)系統(tǒng)全局只需要一個對象實例,比如發(fā)號器肴沫、redis連接對象等
- Spring IOC容器中的bean默認就是單例
- spring boot 中的controller粟害、service、dao層中通過@autowire的依賴注入對象默認都是單例的
-
分類:
- 懶漢:就是所謂的懶加載颤芬,延遲創(chuàng)建對象
- 餓漢:與懶漢相反悲幅,提前創(chuàng)建對象
-
實現(xiàn)步驟
- 私有化構(gòu)造函數(shù)
- 提供獲取單例的方法
單例設(shè)計模式
單例模式中的懶漢實現(xiàn)+雙重檢查鎖定+內(nèi)存模型
代碼:
package net.xdclass;
/**
* @Description 單例設(shè)計模式 - 懶漢實現(xiàn)方式
* @Author 二當家小D
* @Remark
* @Version 1.0
**/
public class SingletonLazy {
private static SingletonLazy instance;
/**
* 構(gòu)造函數(shù)私有化
*/
private SingletonLazy(){}
/**
* 單例對象的方法
*/
public void process(){
System.out.println("方法調(diào)用成功");
}
/**
* 第一種方式
* 對外暴露一個方法獲取類的對象
*
* 線程不安全,多線程下存在安全問題
*
*/
public static SingletonLazy getInstance(){
if(instance == null){
instance = new SingletonLazy();
}
return instance;
}
/**
* 第二種實現(xiàn)方式
* 通過加鎖 synchronized 保證單例
*
* 采用synchronized 對方法加鎖有很大的性能開銷
*
* 解決辦法:鎖粒度不要這么大
*
* @return
*/
public static synchronized SingletonLazy getInstance(){
if(instance == null){
instance = new SingletonLazy();
}
return instance;
}
/**
* 第三種實現(xiàn)方式 也不可以站蝠,存在重復(fù)建對象
* @return
*/
public static SingletonLazy getInstance(){
if(instance == null){
// A汰具、B
synchronized (SingletonLazy.class){
instance = new SingletonLazy();
}
}
return instance;
}
/**
* 第四種實現(xiàn)方式
*
* DCL 雙重檢查鎖定 (Double-Checked-Locking),在多線程情況下保持高性能
*
* 這是否安全,instance = new SingletonLazy(); 并不是原子性操作
* 1菱魔、分配空間給對象
* 2留荔、在空間內(nèi)創(chuàng)建對象
* 3、將對象賦值給引用instance
*
* 假如線程 1-》3-》2順序澜倦,會把值寫會主內(nèi)存聚蝶,其他線程就會讀取到instance最新的值,但是這個是不完全的對象
* (指令重排)
* @return
*/
public static SingletonLazy getInstance(){
if(instance == null){
// A藻治、B
synchronized (SingletonLazy.class){
if(instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
/**
* volatile是Java提供的關(guān)鍵字碘勉,它具有可見性和有序性,
*
* 指令重排序是JVM對語句執(zhí)行的優(yōu)化桩卵,只要語句間沒有依賴验靡,那JVM就有權(quán)對語句進行優(yōu)化
*
* 禁止了指令重排
*/
private static volatile SingletonLazy instance;
public static SingletonLazy getInstance(){
//第一重檢查
if(instance == null){
// A、B 雏节,鎖定
synchronized (SingletonLazy.class){
//第二重檢查
if(instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
}
主代碼:
public class Main {
public static void main(String[] args) {
SingletonLazy.getInstance().process();
}
}
單例模式中的懶漢實現(xiàn)+雙重檢查鎖定+內(nèi)存模型
餓漢設(shè)計模式:
- 餓漢方式:提前創(chuàng)建好對象
- 優(yōu)點:實現(xiàn)簡單胜嗓,沒有多線程同步問題
- 缺點:不管有沒使用,instance對象一直占著這段內(nèi)存
如何選擇:
- 如果對象不大钩乍,且創(chuàng)建不復(fù)雜兼蕊,直接用餓漢的方式即可
- 其他情況則采用懶漢實現(xiàn)方式
代碼:
public class SingletonHungry {
private static SingletonHungry instance = new SingletonHungry();
private SingletonHungry(){}
public static SingletonHungry getInstance(){
return instance;
}
/**
* 單例對象的方法
*/
public void process(){
System.out.println("方法調(diào)用成功");
}
}
``
Main代碼:
public class Main {
public static void main(String[] args) {
//SingletonLazy.getInstance().process();
SingletonHungry.getInstance().process();
}
}
``
單例設(shè)計模式在JDK源碼里面的應(yīng)用
- JDK中Runtime類 餓漢方式
JDK中Runtime類 餓漢方式
Runtime類用于表示虛擬機運行時的狀態(tài), 用于封裝JVM虛擬機進程。每次啟動虛擬機都對應(yīng)一個Runtime實例, 且只有一個實例件蚕。因為該類采用單例模式設(shè)計, 對象不可以直接實例化. 所以要想獲得一個Runtime實例, 只能通過如下方式:
Runtime rt = Runtime.getRuntime();
示例代碼:
public static void main(String[] args) {
// 獲取Runtime實例對象
Runtime rt = Runtime.getRuntime();
System.out.println("處理器個數(shù): "+rt.availableProcessors());
System.out.println("空閑內(nèi)存M數(shù): "+rt.freeMemory()/1024/1024);
System.out.println("最大可用內(nèi)存M數(shù): "+rt.maxMemory()/1024/1024);
}
- JDK中Desktop類 懶漢方式
懶漢方式