JAVA項目實戰-設計模式——單例模式項目中運用


關注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.線程池的使用,線程池使用時,便於管理線程,會定義成全局的。

 

 

     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM