關注Java中技術在項目中的實際運用,能做到學以致用,避免死記硬背的原理。
JAVA設計模式之單例模式
一.設計模式的種類
創建型模式:對象實例化的模式,創建型模式用於解耦對象的實例化過程。
結構型模式:把類或對象結合在一起形成一個更大的結構。
行為型模式:類和對象如何交互,及划分責任和算法。
如下圖所示:
二.單例模式示例代碼
/** * @description: 餓漢式-單例模式 * @author: ZhuCJ * @date: 2020-08-19 12:43 */ public class BadMashSingleClass { /** **************** 餓漢模式 ************************ * 實現方法: * 1.構造器私有化 (目的:保證只能本類中 才能創建出對象) * 2.new 一個此類的靜態對象(目的:靜態屬性資源,存放在JVM的靜態資源區,全局共用一個屬性) * 3.定義一個靜態方法返回靜態對象實例 * 優點: * 1.高並發下能實現對象唯一性 * 2.代碼簡潔,不需要考慮高並發場景 * 缺點: * 1.不能實現對象的延遲加載,加載時就必須創建一個靜態實例,比較消耗資源 */ /** * 定義一個靜態的對象,保證唯一性 */ public static BadMashSingleClass singleClass = new BadMashSingleClass(); /** * 私有化構造器 */ private BadMashSingleClass(){ } /** * 定義一個靜態方法。返回單例對象 * @return */ public static BadMashSingleClass createSingle(){ return singleClass; } } /** * @description: 測試方法 * @author: ZhuCJ * @date: 2020-08-19 13:07 */ public class Main { public static void main(String[] args) { /** * 定義10個線程,每個線程休眠0.5s后創建對象, * 觀察toString方法返回的hashCode值是否相等 * 注:此處創建線程建議采用線程池 */ for (int i =0;i<10;i++){ new Thread(()->{ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } String name = Thread.currentThread().getName(); BadMashSingleClass singleOne = BadMashSingleClass.createSingle(); System.out.println("name:"+name +singleOne.toString()); }).start(); } } } --------------------------------------------- name:Thread-8com.spring.zhucj.rabbitmq.designmode.single.BadMashSingleClass@3673cfa8 name:Thread-4com.spring.zhucj.rabbitmq.designmode.single.BadMashSingleClass@3673cfa8 name:Thread-9com.spring.zhucj.rabbitmq.designmode.single.BadMashSingleClass@3673cfa8 name:Thread-0com.spring.zhucj.rabbitmq.designmode.single.BadMashSingleClass@3673cfa8 name:Thread-5com.spring.zhucj.rabbitmq.designmode.single.BadMashSingleClass@3673cfa8 name:Thread-1com.spring.zhucj.rabbitmq.designmode.single.BadMashSingleClass@3673cfa8 name:Thread-7com.spring.zhucj.rabbitmq.designmode.single.BadMashSingleClass@3673cfa8 name:Thread-3com.spring.zhucj.rabbitmq.designmode.single.BadMashSingleClass@3673cfa8 name:Thread-6com.spring.zhucj.rabbitmq.designmode.single.BadMashSingleClass@3673cfa8 name:Thread-2com.spring.zhucj.rabbitmq.designmode.single.BadMashSingleClass@3673cfa8
/** * @description: 懶漢模式-單例模式 * @author: ZhuCJ 80004071 * @date: 2020-08-19 12:43 */ public class IdlerMashSingleClass { /** * ****************懶漢模式***************** * 實現方法 * 1.構造器私有化 (目的:保證只能本類中 才能創建出對象) * 2.定義一個空的此對象的靜態屬性,並用volatile修飾(指定線程共享保證同步性) * 3.定義一個靜態方法,通過加鎖的方式實現,線程之間同步 * 優點 * 1.可實現創建的對象唯一性 * 2.能延遲加載對象,需要時才會創建 * 缺點 * 1.代碼比較繁瑣 * 2.線程采用加鎖處理,對性能影響比較大 * */ public static volatile IdlerMashSingleClass singleClass = null; /** * 私有化構造器 */ private IdlerMashSingleClass(){ } /** * 方式一 不加鎖,每個線程睡眠1秒后執行創建對象 * 會發現,單線程下創建對象是唯一的, * 如果多個線程同時進入到if(){ }內 還是會創建多個對象 * 缺點,多線程情況下不能保證唯一 * @return */ public static IdlerMashSingleClass createSingleOne(){ if (singleClass == null){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } singleClass = new IdlerMashSingleClass(); } return singleClass; } /** * 方式二 結合加鎖操作 直接對對方法加鎖 * 可以完美解決,但是影響性能,高並發場景對方法進行加鎖會導致訪問時間增加 * 例如:此處如果有1000個用戶訪問這個方法 每個人大於耗時0.01s 那么最后一個人 * 訪問時間是10s,即訪問時間會增加 * @return */ public synchronized static IdlerMashSingleClass createSingleTwo(){ if (singleClass == null){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } singleClass = new IdlerMashSingleClass(); } return singleClass; } /** * 方式三 對對象進行加上鎖 * 缺點,多線程情況下不能保證唯一 * 如果多個線程同時進入到if(){ }內 還是會創建多個對象 * @return */ public static IdlerMashSingleClass createSingleThere(){ if (singleClass == null){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (IdlerMashSingleClass.class){ singleClass = new IdlerMashSingleClass(); } } return singleClass; } /** * 方式四 對對象進行加上鎖后,在對對象進行判斷 * 可以完美解決,但是影響性能,高並發場景對方法進行加鎖會導致訪問時間增加 * @return */ public static IdlerMashSingleClass createSingleFour(){ if (singleClass == null){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (IdlerMashSingleClass.class){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if (singleClass == null){ singleClass = new IdlerMashSingleClass(); } } } return singleClass; } } /** * @description: 測試方法 * @author: ZhuCJ * @date: 2020-08-19 13:07 */ public class Main { public static void main(String[] args) { /** * 定義10個線程,每個線程休眠0.5s后創建對象, * 觀察toString方法返回的hashCode值是否相等 * 注:此處創建線程建議采用線程池 */ for (int i =0;i<10;i++){ new Thread(()->{ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } String name = Thread.currentThread().getName(); IdlerMashSingleClass singleOne = IdlerMashSingleClass.createSingleFour(); System.out.println("name:"+name +singleOne.toString()); }).start(); } } } --------------------------------------- ----1.createSingleOne()方法執行結果 name:Thread-8com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@658afb6a name:Thread-4com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@686e97cd name:Thread-0com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2 name:Thread-6com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@3673cfa8 name:Thread-1com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@3573fdb0 name:Thread-2com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@76575e50 name:Thread-9com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@5a318665 name:Thread-7com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6018d1f6 name:Thread-3com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@47f6725c name:Thread-5com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@7d731227 --------2.createSingleTwo()方法執行結果 name:Thread-2com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@9ec32f5 name:Thread-3com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@9ec32f5 name:Thread-7com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@9ec32f5 name:Thread-4com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@9ec32f5 name:Thread-8com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@9ec32f5 name:Thread-9com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@9ec32f5 name:Thread-1com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@9ec32f5 name:Thread-6com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@9ec32f5 name:Thread-5com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@9ec32f5 name:Thread-0com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@9ec32f5 ----------3.createSingleThree()方法執行結果 name:Thread-6com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@686e97cd name:Thread-2com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2 name:Thread-7com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@7d731227 name:Thread-4com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@76575e50 name:Thread-3com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@3573fdb0 name:Thread-5com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@47f6725c name:Thread-9com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6018d1f6 name:Thread-1com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@9ec32f5 name:Thread-0com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@1f3f7e6a name:Thread-8com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@3673cfa8 ----------4.createSingleFour()方法執行結果 name:Thread-3com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2 name:Thread-7com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2 name:Thread-9com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2 name:Thread-8com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2 name:Thread-5com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2 name:Thread-6com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2 name:Thread-1com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2 name:Thread-2com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2 name:Thread-4com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2 name:Thread-0com.spring.zhucj.rabbitmq.designmode.single.IdlerMashSingleClass@6468d2
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @description: 懶漢模式-單例模式 * @author: ZhuCJ * @date: 2020-08-19 12:43 */ public class LockIdlerMashSingleClass { /** * 哈哈,這個純屬娛樂 * 其實JAVA還提供了除關鍵詞synchronized * 還可以采用concurrent包下的Lock實例ReentrantLock來實現 */ /** * 定義一個靜態的對象,保證唯一性 */ public static LockIdlerMashSingleClass singleClass = null; public static Lock Lock = new ReentrantLock(); /** * 私有化構造器 */ private LockIdlerMashSingleClass(){ } /** * 方式一 不加鎖 * 缺點,多線程情況下不能保證唯一 * 如果多個線程同時進入到if(){ }內 還是會創建多個對象 * @return */ public static LockIdlerMashSingleClass createSingleOne(){ if (singleClass == null){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } singleClass = new LockIdlerMashSingleClass(); } return singleClass; } /** * 方式二 結合加鎖操作 * 可以完美解決,但是影響性能,高並發場景對方法進行加鎖會導致訪問時間增加 * @return */ public static LockIdlerMashSingleClass createSingleTwo(){ Lock.lock(); if (singleClass == null){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } singleClass = new LockIdlerMashSingleClass(); } Lock.unlock(); return singleClass; } /** * 方式三 對對象進行加上鎖 * 缺點,多線程情況下不能保證唯一 * 如果多個線程同時進入到if(){ }內 還是會創建多個對象 * @return */ public static LockIdlerMashSingleClass createSingleThere(){ if (singleClass == null){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } Lock.lock(); singleClass = new LockIdlerMashSingleClass(); Lock.unlock(); } return singleClass; } /** * 方式四 對對象進行加上鎖后,在對對象進行判斷 * 可以完美解決,但是影響性能,高並發場景對方法進行加鎖會導致訪問時間增加 * @return */ public static LockIdlerMashSingleClass createSingleFour(){ if (singleClass == null){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } Lock.lock(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } if (singleClass == null){ singleClass = new LockIdlerMashSingleClass(); } Lock.unlock(); } return singleClass; } }
三.單例模式在項目中的運用場景
1.如高並發場景下,生成客戶訂單號必須保證系統中唯一性,正常我們會根據規則寫一個ID生成器,然后定義一個方法返回字符串。
此時需要保證執行這個方法時保證唯一性,所有可以定義成單例模式
2.對象經常被使用,可能一個對象中多個方法都需要使用。常見是Spring的@AutoWired注入時對象都為單例模式
3.線程池的使用,線程池使用時,便於管理線程,會定義成全局的。