设计模式解析和实现(C++, java)之十-singleton模式

设计模式 - Singleton 单例模式:从设计思想到并发安全与底层实现 Singleton 的学习可以分成四个层次。 阅读详情

 单例模式可以保证一个类有且只有一个实例,并提供一个访问它的全局访问点.在程序设计中,有很多情况需要确保一个类只能有一个实例.从这句话可以 看出,Singleton模式的核心:如何控制用户使用new对一个类的实例构造器的任意调用。如何绕过常规的构造器,提供一种机制来保证一个类只有一个 实例?这应该是类设计者的责任,而不是使用者的责任。

 一、单例模式意图

  保证一个类有且只有一个实例,并提供一个访问它的全局访问点。

 

 二、单例模式UML图

 


图1: Singleton 模式结构
图1: Singleton 模式结构

 

实现 Singleton 模式的办法通常有三种:
  1.用静态方法实现 Singleton;
  2.以静态变量为标志实现 Singleton;
  3.用注册器机制来创建 Singleton。

 

三 实现Java Singleton的方案

Java Singleton是指在特定系统范围内只能实例化一次的Java类,如何理解特定系统范围?按照需求和环境定义系统范围,关注在特定系统范围内单一实例的需求:

系统范围和环境定义 方案编号
框架容器内 A
单一JVM中、单一类加载器加载类 B、C、D
单一JVM、不同类加载器加载相同类  
系统跨多个JVM  

 

A

提供实例管理容器的第三方框架,例如Spring IOC容器,可以通过配置保证实例在容器内的唯一性。这些被外部容器管理的类,能在某个容器范围内达到Singleton的效果,不一定禁止自身在容器外 部范围生成多个实例。 这种方案可以看作是一种局部的单例解决方案。

B

代码示范如下

B

C

C

D

有时候类的实例化开销较为昂贵,有时候类的实例化要用到系统运行时的动态数据做参数等等,在上述情形,静态成员变量需要延迟初始化。程序员中间流传较广的一种形式是:

D_01

这种写法最大的问题是getInstance()方法被多个线程竞争使用的时候,可能会产生多个实例,违反了单例设计的初衷。如果多个实例的风险(Singleton失败)不会引起您的系统异常,比如实例存放的是无状态的数据、实例是轻量级的,您可以坚持这种写法。

当然还有改进的方法,一般是在该方法前加上“synchronized”关键字:

D_02

这种作法的副作用就是影响性能。

双检查锁(Double-Checked Locking)是在多线程环境下实现延迟初始化的有效方式(如C++),不幸的是,对大多数JVM是无效的。有一篇文章解释的很深入:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

其他方案

单一JVM、不同类加载器加载相同类或跨多个JVM的情形,要保证Java Singleton不失败,我还没找到恰当的方法。

有些人认为单例的需求不仅仅是来自某些组件自身的唯一特性,还来自对创建并维护多个对象产生消耗的无法忍受、来自对性能的追求。如果出于降低消耗和 提高性能的目的,很多无状态的类(类的所有实例天然是始终相同的),即便面临单一JVM、不同类加载器加载相同类或跨多个JVM的情形,也可以采用上述的 Singleton实现方式,可以尽量减少实例的数量。

当Java Singleton遇到反序列化

一个序列化的实例,每次反序列化的时候都会产生一个新的实例。Singleton也不例外。

我们看看一个例子:

001

进行序列化测试

public static void main(String[] args) throws Exception{
         //序列化
          ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("E://kingBedroom.obj"));
          King king_1 = King.INSTANCE;         
          objectOutputStream.writeObject(king_1);
          objectOutputStream.close();
          //反序列化
          ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("E://kingBedroom.obj"));
          King king_2 = (King)objectInputStream.readObject();
          objectInputStream.close();
          //比较是否原来的实例
          System.out.println(king_1==king_2);

    }

结果是false

解决方法是为King类增加readResolve()方法:

private Object readResolve(){
        return INSTANCE;
    }

反序列化之后新创建的对象会先调用此方法,该方法返回的对象引用被返回,取代了新创建的对象。本质上,该方法忽略了新建对象,仍然返回类初始化时创建的那个实例。

 

 

五:C++的设计与实现:

Singleton 模式是常用的设计模式之一,但是要实现一个真正实用的设计模式却也不是件容易的事情。

1.         标准的实现

class Singleton

{

public:

       static Singleton * Instance()

       {

              if( 0 == _instance)

              {

                     _instance = new Singleton;

              }

              return _instance;

       }

protected:

       Singleton(void)

       {

       }

       virtual ~ Singleton(void)

       {

       }

       static Singleton* _instance;

};

       这是教科书上使用的方法。看起来没有什么问题,其实包含很多的问题。下面我们一个一个的解决。

2.         自动垃圾回收

上面的程序必须记住在程序结束的时候,释放内存。为了让它自动的释放内存,我们引入 auto_ptr 改变它。

#include <memory>

#include <iostream>

using namespace std;

class Singleton

{

public:

       static Singleton * Instance()

       {

              if( 0 == _instance. get())

              {

                     _instance. reset( new Singleton);

              }

              return _instance. get();

       }

protected:

       Singleton(void)

       {

              cout << "Create Singleton" << endl;

       }

       virtual ~ Singleton(void)

       {

              cout << "Destroy Singleton" << endl;

       }

       friend class auto_ptr< Singleton>;

       static auto_ptr< Singleton> _instance;

};

//Singleton.cpp

auto_ptr< Singleton> Singleton:: _instance;

3.         增加模板

在我的一个工程中,有多个的 Singleton 类,对 Singleton 类,我都要实现上面这一切,这让我觉得烦死了。于是我想到了模板来完成这些重复的工作。

现在我们要添加本文中最吸引人单件实现:

/********************************************************************

    (c) 2003-2005 C2217 Studio

    Purpose:    Implement singleton pattern

    History:

*********************************************************************/

#pragma once

 

#include <memory>

using namespace std;

using namespace C2217:: Win32;

 

namespace C2217

{

namespace Pattern

{

template <class T>

class Singleton

{

public:

       static inline T* instance();

      

private:

       Singleton(void){}

       ~ Singleton(void){}

       Singleton(const Singleton&){}

       Singleton & operator= (const Singleton &){}

 

       static auto_ptr< T> _instance;

};

 

template <class T>

auto_ptr< T> Singleton< T>:: _instance;

 

template <class T>

  inline T* Singleton< T>:: instance()

{

       if( 0 == _instance. get())

       {

              _instance. reset ( new T);

       }

      

       return _instance. get();

}

 

//Class that will implement the singleton mode,

//must use the macro in it's delare file

#define DECLARE_SINGLETON_CLASS( type ) /

       friend class auto_ptr< type >; /

       friend class Singleton< type >;

}

}

 

4.         线程安全

上面的程序可以适应单线程的程序。但是如果把它用到多线程的程序就会发生问题。主要的问题在于同时执行 _instance. reset ( new T); 就会同时产生两个新的对象,然后马上释放一个,这跟 Singleton 模式的本意不符。所以,你需要更加安全的版本:

/********************************************************************

    (c) 2003-2005 C2217 Studio

    Purpose:    Implement singleton pattern

    History:

*********************************************************************/

#pragma once

 

#include <memory>

using namespace std;

#include "Interlocked.h"

using namespace C2217:: Win32;

 

namespace C2217

{

namespace Pattern

{

template <class T>

class Singleton

{

public:

       static inline T* instance();

      

private:

       Singleton(void){}

       ~ Singleton(void){}

       Singleton(const Singleton&){}

       Singleton & operator= (const Singleton &){}

 

       static auto_ptr< T> _instance;

       static CResGuard _rs;

};

 

template <class T>

auto_ptr< T> Singleton< T>:: _instance;

 

template <class T>

CResGuard Singleton< T>:: _rs;

 

template <class T>

  inline T* Singleton< T>:: instance()

{

       if( 0 == _instance. get() )

       {

              CResGuard:: CGuard gd( _rs);

              if( 0 == _instance. get())

              {

                     _instance. reset ( new T);

              }

       }

       return _instance. get();

}

 

//Class that will implement the singleton mode,

//must use the macro in it's delare file

#define DECLARE_SINGLETON_CLASS( type ) /

       friend class auto_ptr< type >; /

       friend class Singleton< type >;

}

}

       CresGuard 类主要的功能是线程访问同步 , 代码如下:

/******************************************************************************

Module:  Interlocked.h

Notices: Copyright (c) 2000 Jeffrey Richter

******************************************************************************/

 

 

#pragma once

///////////////////////////////////////////////////////////////////////////////

 

// Instances of this class will be accessed by multiple threads. So,

// all members of this class (except the constructor and destructor)

// must be thread-safe.

class CResGuard {

public:

   CResGuard()   { m_lGrdCnt = 0 ; InitializeCriticalSection(& m_cs); }

   ~ CResGuard() { DeleteCriticalSection(& m_cs); }

 

   // IsGuarded is used for debugging

   BOOL IsGuarded() const { return( m_lGrdCnt > 0 ); }

 

public:

   class CGuard {

   public:

      CGuard( CResGuard& rg) : m_rg( rg) { m_rg. Guard(); };

      ~ CGuard() { m_rg. Unguard(); }

 

   private:

      CResGuard& m_rg;

   };

 

private:

   void Guard()    { EnterCriticalSection(& m_cs); m_lGrdCnt++; }

   void Unguard() { m_lGrdCnt--; LeaveCriticalSection(& m_cs); }

 

   // Guard/Unguard can only be accessed by the nested CGuard class.

   friend class CResGuard:: CGuard;

 

private:

   CRITICAL_SECTION m_cs;

   long m_lGrdCnt;    // # of EnterCriticalSection calls

};

 

 

///////////////////////////////////////////////////////////////////////////////

5.         实用方法

比如你有一个需要实现单件模式的类,就应该这样实现:

#pragma once

#include "singleton.h"

using namespace C2217:: Pattern;

 

class ServiceManger

{

public:

       void Run()

       {

       }

private:

       ServiceManger(void)

       {

       }

       virtual ~ ServiceManger(void)

       {

       }

       DECLARE_SINGLETON_CLASS( ServiceManger);

};

 

typedef Singleton< ServiceManger> SSManger;

 

在使用的时候很简单,跟一般的 Singleton 实现的方法没有什么不同。

int _tmain(int argc, _TCHAR* argv[])

{

        SSManger:: instance()-> Run();

}

 

 

腾讯四大方向面经汇总:前端、Java、C++、Go全解析 技术面试的底层逻辑,往往藏在各语言、框架的表象之下。无论是浏览器渲染机制、JVM内存模型、C++的RAII与移动语义,还是Go的GMP并发调度,大厂面试官真正考察的都是候选人“理解原理并落地决策”的能力。并发编程、性能优化、容器与数据结构这些高频考点,在不同技术栈中呈现出共通的考察维度——从基础八股到项目深挖,从手写代码到方案选型,每轮面试都在验证你能否在复杂工程中做出正确取舍。本文基于真实面试经历,完整复盘腾讯前端、Java、C++、Go四个方向的核心题目、追问链与答题思路,并横向对比各方向考察重点,提炼 阅读详情

相关推荐

GT-suite v2016的下载和安装

1 写在前面 GT-suite 是一款用来做内燃机仿真的软件。在安装过程中遇到很多问题,网上的安装教程也大都抄来抄去,试着安装了几遍都没有成功。最终在贴吧一个老哥的帖子里找到了解决办法,但是老哥的帖子也不是很详细,所以做了这个详细版本的教程。 GT-suite v2016安装包: 链接:https://pan.baidu.com/s/1JZPgJ8_-J-L8rV84-eHedQ 提取码:uj...

kisssshot的博客 3万+

C++设计模式实战:从Java到C++的思维转换与RAII应用

设计模式是解决面向对象设计中常见问题的可复用方案,其核心思想在于通过封装变化、松耦合等原则提升代码的可维护性和扩展性。在C++中实现这些模式,需要深刻理解其与Java等托管语言的关键差异,特别是内存管理和对象模型。C++没有垃圾回收机制,因此必须采用RAII(资源获取即初始化)原则,利用智能指针(如std::unique_ptr、std::shared_ptr)来管理对象的生命周期,这是构建健壮系统的基石。例如,在实现策略模式或观察者模式时,通过智能指针明确资源所有权,可以避免内存泄漏和悬垂指针问题。这些技

java2000.net 368

用野火FPGA开发板(EP4CE10)手把手教你实现一个高精度频率计(附完整Verilog代码)

本文详细介绍了基于野火FPGA开发板(EP4CE10)实现高精度频率计的全过程,涵盖等精度测量法的核心原理、Verilog代码实现及优化技巧。通过实际案例演示,帮助开发者掌握1Hz-50MHz范围、精度达0.1%的频率计设计方法,并提供完整的工程代码和实测数据。

weixin_29261711的博客 439

在序列化、反序列化下如何保持单例(Singleton)模式

在 Java 中,当一个对象被序列化后再被反序列化,通常情况下会创建一个新的对象实例。在这个过程中,通常会使用类的构造函数创建一个新的对象,并将保存的状态设置给这个新对象。这意味着,默认情况下,在序列化和反序列化过程中,会产生新的对象实例,而不是保持原有的对象实例。解决上述代码的这个问题,可以在类中实现 readResolve() 方法,确保在反序列化时返回同一个对象实例,从而维护对象的单例特性。枚举类的实例创建是线程安全的,而且枚举类不会被反射破坏,并且可以处理序列化和反序列化,保证了单例模式的可靠性。

FLGB 1565

闲鱼x-sign、x-mini-wua、x-umt三字段协同验证机制解析

在移动客户端风控体系中,设备标识与行为签名是保障接口安全的核心技术。x-sign并非传统时间戳+密钥的静态签名,而是融合设备特征、运行时状态与服务端动态因子的多阶段位运算结果;x-mini-wua作为小程序轻量水印,依赖主App注入的短时效sessionKey与截断时间戳;x-umt则通过设备硬件与网络环境联合编码实现强绑定。三者构成‘设备—环境—行为’三位一体的交叉验证链路,广泛应用于电商API防护、二手平台数据采集及自动化运营等场景。本文基于Android/iOS真机逆向与Frida动态分析,揭示其生成

weixin_30457551的博客 422

从零构建高精度电子秤:HX711与STM32的时序通信陷阱与实战避坑指南

本文详细解析了使用STM32F103C8T6与HX711模块构建高精度电子秤的通信时序陷阱与实战解决方案。重点探讨了微秒级延迟实现、数据读取算法、校准方法和滤波优化,帮助开发者避开常见通信问题,提升电子秤的测量精度和稳定性。

data3的博客 782

Python实现本地WiFi密码管理工具:原理、代码与安全实践

在计算机网络管理中,系统凭证的安全存储与访问是基础而重要的技术环节。操作系统通常将已连接WiFi的SSID与密码加密保存在本地配置文件中,通过系统级命令(如Windows的netsh或macOS的security)可进行授权访问。Python的subprocess模块提供了调用系统命令的能力,结合正则表达式解析输出,即可实现自动化凭证提取。这种技术方案的价值在于将繁琐的手动查询过程自动化,提升个人设备管理的效率,同时完全在本地执行,无网络传输风险。其典型应用场景包括:合法用户忘记密码时快速找回、设备重装前的

weixin_33863087的博客 435

【对比学习+分割】CRIS:CLIP驱动RIS图像分割(CVPR 2022)

CRIS: CLIP-Driven Referring Image Segmentation

这个博客太懒了什么都没说 3325

将c++代码改成java代码实验总结

(1)Java里面没有namespace,有package。而且Java里不用写头文件。 (2)private,public等在Java里面需要单独写在每一个成员前面。 private: //原代码节选 int id; //账号 double balance; //余额 double rate; //存款的年利率 int lastDate; //上次变更余额的时期 double accumulation; //余额按日累加之和 //改写后

qq_45841720的博客 3698

MongoDB启动失败(service mongod start):Failed to unlink socket file /tmp/mongodb-27017.sock Unknown error

错误日志(/var/log/mongodb/mongo.log): 2019-09-16T06:00:00.251+0000 I CONTROL [main] ***** SERVER RESTARTED ***** 2019-09-16T06:00:00.253+0000 I CONTROL [main] Automatically disabling TLS 1.0, to force-e...

deantzzz的博客 1838

STM32硬件SPI驱动W5500以太网模块全流程(附避坑指南)

本文详细介绍了STM32硬件SPI驱动W5500以太网模块的全流程,包括硬件架构设计、SPI接口选型、官方驱动移植、网络协议栈配置与优化等关键步骤。通过实战案例和性能对比数据,帮助开发者快速实现稳定高效的网络连接,特别提供了常见问题排查指南和性能调优技巧。

weixin_29194693的博客 268

java实验一(C++改写成Java)

一,4-9.cpp和5-11.cpp 在改写银行管理系统中,只需将c++代码做些微改动, 1.c++中的const在java中改为final,当类被修饰为final时,这个类不能被继承,当一个方法被修饰为final时,这个方法不可以被重写; 2.保留两位小数时,在Java中用了DecimalFormat 当初我直接这样在表达式中用format,(下图) 由于amount定义为double类型...

qq_43797186的博客 955

深入解析MTK平台hang_detect机制:从原理到实战调试

本文深入解析了MTK平台hang_detect机制的工作原理与实战调试技巧。作为系统健康监测的核心机制,hang_detect通过多层级监控体系检测应用层和内核层异常,结合Kernel_exception等日志信息快速定位问题。文章详细介绍了典型问题案例、调试方法及预防措施,帮助开发者提升MTK平台系统稳定性调试能力。

purple的博客 1004

C++与JAVA中的单例模式

C++与JAVA中的单例模式 起源 在内存中只有一个对象,节省内存空间。避免频繁的创建销毁对象,可以提高性能。避免对共享资源的多重占用。可以全局访问。 确保一个类只有一个实例,自行实例化并向系统提供这个实例 问题 线程安全问题 资源使用问题 实现 方式1:饿汉式 优点:在未调用getInstance() 之前,实例就已经创建了,天生线程安全 缺点:如果一直没有调用getInstance(...

qq495553087的博客 531

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

探究Singleton设计模式

Singleton通常被认为是最简单的设计模式,很多初学者都是通过它来了解设计模式的含义。然而,熟悉设计模式的技术人员都知道,要正确实现Singleton模式实际上是非常难的,涉及到很多技术细节。本文对于Singleton做了大胆深入的研究,并且探讨了C++、Java和C#中的Singleton实现。 在开发软件应用程序过程中,随着应用程序的开发,会出现重复性的模式。随着整个软件系统的开...

aome1470的博客 206

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

C#设计模式之Singleton

近来,我在学习如何在C#语言中使用设计模式中读到一些资料,其中有关Singleton设计模式引起了我的注意。 学过设计模式的开发者都知道Singleton模式。我想简要地解释一下这个设计模式是为那些尚未学习过设计模式的朋友。Singleton设计模式是告诉您如何在你的应用程序创建一个唯一类示例的全局对象,也就是说,这个对象只能被实例化一次,这个对象同时提供一个访问它的一个全局的...

aao84421的博客 142

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

书评--设计模式入门

个别问题的最佳解答成熟的产业,应该有很多经典的设计,广为流传的想法,共通的价值。由于信息硬件突飞猛进,信息应用面快速深入所有环节,导致需求多样与设计复杂。在开发与使用软件时,我们期待各种问题的最佳解答。十多年前,Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides (由于太有名了,一般称之为四人帮 Gang of Four GoF)仿效建筑师

移动开发与培训 286

C++设计模式实战解析:从核心思想到工程应用

设计模式是软件工程中解决常见设计问题的经典方案,其核心思想在于识别并封装变化点,从而提升代码的可维护性和扩展性。从原理上看,设计模式通过抽象与多态、组合优于继承等原则,将易变的实现细节与稳定的接口分离。在C++开发中,这一技术价值尤为突出,它能有效管理对象生命周期、组织复杂类结构、协调对象间协作,从而应对内存安全、性能优化和系统解耦等核心挑战。结合C++的RAII、智能指针、模板等特性,设计模式可广泛应用于游戏引擎、高性能服务器、桌面客户端等场景,帮助开发者构建健壮且灵活的软件架构。本文以工厂方法和观察者模

406

C++单例模式 (mind笔记—全代码示例与解析)

单例模式 C++

m0_53215519的博客 671

任务管理系统低层设计(LLD)实战:Java 状态机、观察者与策略模式在 Task Management System 中的落地

本文以 awesome-low-level-design 仓库中的[任务管理系统 LLD 文档](https://link.gitcode.com/i/995b83c391b817091139de6e98bb5f58)为骨架,结合其完整的 Java 源码实现,系统讲解如何从需求出发设计一个支持任务创建、分配、状态流转、优先级与评论的 Task Management System。读完本文,你将掌握

gitblog_00588的博客 391

单例模式全解析:线程安全、懒加载与反射防御的实现方法

软件设计模式中,单例模式因其全局唯一实例特性,成为高频工程实践与面试焦点。其核心价值是控制共享资源、避免重复初始化,但在Java、C++、C#等语言里扎实落地并不简单,涉及类加载机制、内存可见性、指令重排、反射与序列化边界。饿汉式最简单,却牺牲了懒加载;懒汉式、双重检查锁(DCL)需要恰当的内存屏障(如volatile)才能安全;静态内部类与Meyers' Singleton借语言机制兼顾懒加载与安全;枚举是Java场景抵御反射的最佳方案。理解这些底层原理,有助于在作业、面试和业务系统中合理选型,避开全局状

weixin_30846347的博客 234
上一篇: linux kernel map
下一篇: android的binder机制研究二
landerluu
博客等级 码龄20年 22粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值