主頁 > 軟體設計 > 【單例設計模式原理詳解】Java/JS/Go/Python/TS不同語言實作

【單例設計模式原理詳解】Java/JS/Go/Python/TS不同語言實作

2023-04-27 10:32:38 軟體設計

簡介

單例模式(Singleton Pattern)屬于創建型設計模式,這種模式只創建一個單一的類,保證一個類只有一個實體,并提供一個訪問該實體的全域節點,

當您想控制實體數目,節省系統資源,并不想混用的時候,可以使用單例模式,單例有很多種實作方式,主要分為懶漢和餓漢模式,同時要通過加鎖來避免執行緒安全,不同語言的單例實作略有差異,可以通過查看不同版本的原始碼來深入理解其中的差異,

作用

  1. 避免全域使用的類頻繁地創建與銷毀,
  2. 保證一個類僅有一個實體,并提供一個訪問它的全域訪問點,

實作步驟

  1. 創建單例類,注意執行緒安全
  2. 回傳全域唯一實體

UML

 

Java代碼

單例實作,不同語言有很大不同,跟語言特性有關,請查看其他原始碼進行比較,

餓漢式(執行緒安全)

// SingletonEager.java 當類被加載的時候會初始化,靜態變數被創建并分配記憶體空間 
public class SingletonEager {
  private String name = "SingletonEager";
  // 類加載時就初始化,浪費記憶體
  private static final SingletonEager instance = new SingletonEager();

  // 建構式是private,不允許實體化
  private SingletonEager() {

  }
  public static SingletonEager getInstance() {
    return instance;
  }

  public void run() {
    System.out.println("SingletonEager::run() " + this.name);
  }
}

飽漢式

// SingletonLazy.java 懶漢式也叫飽漢式,增加synchronized來保證執行緒安全
public class SingletonLazy {

  private static SingletonLazy instance;
  private String name;

  private SingletonLazy() {

  }

  // 類初始化時,靜態變數static的instance未被創建并分配記憶體空間
  // 當getInstance方法第一次被呼叫時,再初始化instance變數,并分配記憶體
  // 相當于延遲到呼叫時再實體化,加synchronized以便執行緒安全,不加則存在并發時多個實體的情形
  public static synchronized SingletonLazy getInstance(String name) {
    if (instance == null) {
      instance = new SingletonLazy();
      instance.name = name;
    }
    return instance;
  }

  public void run() {
    System.out.println("SingletonLazy::run() " + this.name);
  }
}

靜態內部類

// SingletonInner.java 靜態內部類方式,既實作延遲加載,也保障執行緒安全,
public class SingletonInner {

  private String name;

  private SingletonInner() {

  }

  // 靜態內部類利用了類加載初始化機制,外部類加載時,并不會加載內部類,也不會執行
  // 虛擬機會保證方法在多執行緒環境下使用加鎖同步,只會執行一次,因此執行緒安全
  private static class Inner {
    private static final SingletonInner instance = new SingletonInner();
  }

  // 當執行getInstance()方法時,虛擬機才會加載靜態內部類
  public static SingletonInner getInstance(String name) {
    if (Inner.instance.name == null) {
      Inner.instance.name = name;
    }
    return Inner.instance;
  }

  public void run() {
    System.out.println("SingletonInner::run() " + this.name);
  }
}

雙重檢驗懶漢

// SingletonDoubleCheck.java 雙重檢驗懶漢單例,單例模式最優方案,執行緒安全并且效率高 
public class SingletonDoubleCheck {

  // 定義一個靜態私有變數(不初始化,不使用final關鍵字)
  // 可以使用volatile保證多執行緒訪問時變數的可見性
  // 這樣避免了初始化時其他變數屬性還沒賦值完時,被另外執行緒呼叫
  private static volatile SingletonDoubleCheck instance;
  private String name;
  private SingletonDoubleCheck() {

  }

  // 延遲到呼叫時實體化
  public static SingletonDoubleCheck getInstance(String name) {
    if (instance == null) {
      // 在實體化時再synchronized
      synchronized (SingletonDoubleCheck.class) {
        if (instance == null) {
          instance = new SingletonDoubleCheck();
          instance.name = name;
        }
      }
    }
    return instance;
  }

  public void run() {
    System.out.println("SingletonDoubleCheck::run() " + this.name);
  }
}

測驗呼叫

    /**
     * 單例模式就是一個類只創建一個實體,以便節省開銷和保證統一
     * 對于多執行緒語言需要注意執行緒安全和性能之間取得一個平衡
     */
    SingletonEager singletonEager1 = SingletonEager.getInstance();
    SingletonEager singletonEager2 = SingletonEager.getInstance();
    singletonEager1.run();
    singletonEager2.run();
    // 兩個實體相等
    System.out.println("singletonEager1 == singletonEager2 ? " + String.valueOf(singletonEager1 == singletonEager2));

    /*********************** 分割線 ******************************************/

    SingletonLazy singletonLazy1 = SingletonLazy.getInstance("singletonLazy1");
    SingletonLazy singletonLazy2 = SingletonLazy.getInstance("singletonLazy2");
    singletonLazy1.run();
    singletonLazy2.run();

    /*********************** 分割線 ******************************************/

    SingletonDoubleCheck singletonDoubleCheck1 = SingletonDoubleCheck.getInstance("singletonDoubleCheck1");
    SingletonDoubleCheck singletonDoubleCheck2 = SingletonDoubleCheck.getInstance("singletonDoubleCheck2");
    singletonDoubleCheck1.run();
    singletonDoubleCheck2.run();

    /*********************** 分割線 ******************************************/

    SingletonInner singletonInner1 = SingletonInner.getInstance("singletonInner1");
    SingletonInner singletonInner2 = SingletonInner.getInstance("singletonInner2");
    singletonInner1.run();
    singletonInner2.run();

Go代碼

// DoubleCheckSingleton.go
import (
  "fmt"
  "sync"
)

// 安全懶漢模式的升級版,通過sync的Mutex實作雙重檢驗
type DoubleCheckSingleton struct {
  name string
}

func (s *DoubleCheckSingleton) Run() {
  fmt.Println("DoubleCheckSingleton::run()", s.name)
}

// 定義私有變數,用來保存實體
var doubleCheckSingletonInstance *DoubleCheckSingleton
var lock = &sync.Mutex{}

// 是懶漢模式安升級版,雙重檢查來來支持延遲實體化單例物件
func GetDoubleCheckSingletonInstance(name string) *DoubleCheckSingleton {
  // 未實體化才進行加鎖
  if doubleCheckSingletonInstance == nil {
    lock.Lock()
    defer lock.Unlock()
    // 為了保險,鎖住之后再次檢查是否已實體化
    if doubleCheckSingletonInstance == nil {
      doubleCheckSingletonInstance = &DoubleCheckSingleton{}
      doubleCheckSingletonInstance.name = name
    }
  }

  return doubleCheckSingletonInstance
}

JS版本

// LazySingleton.js
export class LazySingleton {
  static instance
  constructor(alias) {
    this.alias = alias
  }

  // 懶漢模式,延遲實體化,請求實體時判斷,如果已經實體化過就直接回傳
  // js是單執行緒語言,無需考慮多執行緒問題
  static getInstance(alias) {
    if (this.instance === undefined) {
      this.instance = new LazySingleton(alias)
    }
    return this.instance
  }

  run() {
    console.log('LazySingleton::run()', this.alias)
  }
}

Python語言

# SingletonSafe.py
from threading import Lock, Thread


# 加鎖的基于元類的單例模式,基于元類type創建的加強版
class SingletonMeta(type):
    # 執行緒安全單例模式,適用python3
    _instances = {}

    _lock: Lock = Lock()

    def __call__(cls, *args, **kwargs):
        with cls._lock:
            if cls not in cls._instances:
                instance = super().__call__(*args, **kwargs)
                cls._instances[cls] = instance
        return cls._instances[cls]

# 繼承SingletonMeta就是單例
class SingletonSafe(metaclass=SingletonMeta):
    name: str = None

    def __init__(self, name: str) -> None:
        self.name = name

    def run(self):
        print('SingletonSafe::run()', self.name)

C語言

// lazy_singleton_safe.c
#include "func.h"
#include <pthread.h>

// 靜態指標,未被創建并分配記憶體空間,指向唯一實體
static LazySingletonSafe *lazy_singleton_safe_instance = NULL;

void lazy_singleton_safe_run(LazySingletonSafe *singleton)
{
  printf("\r\n LazySingletonSafe::run() [name=%s value=https://www.cnblogs.com/letjs/p/%d]", singleton->name, singleton->value);
}

// 內部私有實體化函式,不公開
static LazySingletonSafe *new_lazy_singleton_safe(char *name)
{
  LazySingletonSafe *singleton = (LazySingletonSafe *)malloc(sizeof(LazySingletonSafe));
  strcpy(singleton->name, name);
  singleton->run = &lazy_singleton_safe_run;
  return singleton;
}

// 宣告鎖
pthread_mutex_t singleton_lock;

// 非執行緒安全懶漢模式,延遲初始化,多個執行緒同時呼叫函式時, 可能會被初始化多次,存在執行緒不安全問題
LazySingletonSafe *get_lazy_singleton_safe_instance(char *name)
{
  printf("\r\n get_lazy_singleton_safe_instance() [name=%s]", name);
  if (pthread_mutex_init(&singleton_lock, NULL) != 0)
  {
    perror("error init mutext:");
  }

  // 通過加鎖來防止執行緒并發導致的不安全
  if (lazy_singleton_safe_instance == NULL)
  {
    printf("\r\n new instance [name=%s]", name);
    pthread_mutex_lock(&singleton_lock);
    lazy_singleton_safe_instance = new_lazy_singleton_safe(name);
    pthread_mutex_unlock(&singleton_lock);
  }
  return lazy_singleton_safe_instance;
}

更多語言版本

不同語言實作設計模式:https://github.com/microwind/design-pattern

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/551374.html

標籤:設計模式

上一篇:系統認知篇:防腐層、門面模式及適配模式的本質

下一篇:返回列表

標籤雲
其他(158236) Python(38107) JavaScript(25396) Java(18003) C(15217) 區塊鏈(8260) C#(7972) AI(7469) 爪哇(7425) MySQL(7151) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5870) 数组(5741) R(5409) Linux(5332) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4564) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2431) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1961) Web開發(1951) HtmlCss(1928) python-3.x(1918) 弹簧靴(1913) C++(1912) xml(1889) PostgreSQL(1874) .NETCore(1855) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【單例設計模式原理詳解】Java/JS/Go/Python/TS不同語言實作

    簡介 單例模式(Singleton Pattern)屬于創建型設計模式,這種模式只創建一個單一的類,保證一個類只有一個實體,并提供一個訪問該實體的全域節點。 當您想控制實體數目,節省系統資源,并不想混用的時候,可以使用單例模式。單例有很多種實作方式,主要分為懶漢和餓漢模式,同時要通過加鎖來避免執行緒安 ......

    uj5u.com 2023-04-27 10:32:38 more
  • 系統認知篇:防腐層、門面模式及適配模式的本質

    門面模式和配接器模式是代碼級的設計模式,而防腐層本質是一種防御型策略,在更高的層級對系統進行解耦。通常情況下,防腐層包含一系列的門面類和配接器類以及一些轉換器類。 ......

    uj5u.com 2023-04-27 08:47:06 more
  • 系統認知篇:防腐層、門面模式及適配模式的本質

    門面模式和配接器模式是代碼級的設計模式,而防腐層本質是一種防御型策略,在更高的層級對系統進行解耦。通常情況下,防腐層包含一系列的門面類和配接器類以及一些轉換器類。 ......

    uj5u.com 2023-04-27 08:46:30 more
  • 5 種微服務網關介紹

    微服務網關作為微服務后端服務的統一入口,它可以統籌管理后端服務,主要分為資料平面和控制平面:

    資料平面主要功能是接入用戶的HTTP請求和微服務被拆分后的聚合。使用微服務網關統一對外暴露后端服務的API和契約,路由和過濾功能正是網關的核心能力模塊。另外,微服務網關可以實作攔截機制和專注跨橫切面的功能... ......

    uj5u.com 2023-04-26 09:34:17 more
  • 5 種微服務網關介紹

    微服務網關作為微服務后端服務的統一入口,它可以統籌管理后端服務,主要分為資料平面和控制平面:

    資料平面主要功能是接入用戶的HTTP請求和微服務被拆分后的聚合。使用微服務網關統一對外暴露后端服務的API和契約,路由和過濾功能正是網關的核心能力模塊。另外,微服務網關可以實作攔截機制和專注跨橫切面的功能... ......

    uj5u.com 2023-04-26 09:17:33 more
  • 突破傳統監測模式:業務狀態監控HM的新思路

    在系統架構設計中非常重要的一環是要做資料監控和資料最終一致性,這里主要講如何去補償?補償的方案哪些?這就引出來資料監控系統了。有小伙伴會問了,為什么業務狀態監控系統可以做補償?別急,且看本文。 ......

    uj5u.com 2023-04-26 08:17:54 more
  • 虛擬存盤管理中幾種缺頁中斷演算法計算邏輯

    題目一:在一個采用頁式虛擬存盤管理的系統中,有一用戶作業,它依次要訪問的頁面序列是1,2,3,4,1,2,5,1,2,3,4,5.假定分配給該作業的頁數為3且作業初始時未裝載頁面,那么采用FIFO調度演算法產生的缺頁中斷數為多少,采用LRU調度演算法產生的缺頁中斷數為多少? 決議: FIFO調度演算法:先 ......

    uj5u.com 2023-04-26 08:17:36 more
  • 突破傳統監測模式:業務狀態監控HM的新思路

    在系統架構設計中非常重要的一環是要做資料監控和資料最終一致性,這里主要講如何去補償?補償的方案哪些?這就引出來資料監控系統了。有小伙伴會問了,為什么業務狀態監控系統可以做補償?別急,且看本文。 ......

    uj5u.com 2023-04-26 08:16:26 more
  • 虛擬存盤管理中幾種缺頁中斷演算法計算邏輯

    題目一:在一個采用頁式虛擬存盤管理的系統中,有一用戶作業,它依次要訪問的頁面序列是1,2,3,4,1,2,5,1,2,3,4,5.假定分配給該作業的頁數為3且作業初始時未裝載頁面,那么采用FIFO調度演算法產生的缺頁中斷數為多少,采用LRU調度演算法產生的缺頁中斷數為多少? 決議: FIFO調度演算法:先 ......

    uj5u.com 2023-04-26 08:16:09 more
  • 【代理設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    簡介 代理模式(Proxy Pattern)是一種結構型設計模式,用一個類來代理另一個類或幾個類的功能。 在代理模式中,我們創建具有現有物件的物件,以便向外界提供功能介面。 延遲初始化(虛擬代理)。如果你有一個偶爾使用的重量級服務物件,一直保持該物件運行會消耗系統資源時,可使用代理模式。 訪問控制( ......

    uj5u.com 2023-04-25 09:42:43 more