singleton pattern的推荐实现

简介:

一、单例模式的C#实现:

(1)使用double-checked locking的方式:

复制代码
public sealed class Singleton
{
    private static volatile Singleton instance = null;
    private static readonly object padlock = new object();

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            if (instance==null)
            {
                lock (padlock)
                {
                    if (instance==null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
}
复制代码

这种实现方式对多线程来说是安全的,同时线程不是每次都加锁,只有判断对象实例没有被创建时它才加锁。注意volatile的使用,只有加了volatile修饰,才能保证在多线程环境下只有一个实例。

因为使用了锁,没有下面的方式高效。

复制代码
public sealed class Singleton
{
    private static Singleton instance = null;

    private Singleton()
    {
    }

    public static Singleton GetInstance()
    {
        if(instance!=null) return instance;
        
        Singleton temp = new Singleton();
        Interlocked.CompareExchange(ref instance,temp,null);
        
        return instance;
    }
}
复制代码

如果多个线程同时调用GetInstance(),会产生多个Singleton对象,但是Interlocked.CompareExchange()会保证只有一个引用赋值给instance,没有引用到的对象会被垃圾回收。

(2)使用静态成员初始化方式(thread-safe without using locks)

复制代码
public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton()
    {
    }

    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}
复制代码

由于Instance是static的,保证在AppDomain只有一个实例。

初始化的线程安全性是由.Net保证的。

手动加上一个static的constructor,以让.Net对它进行延迟加载,但并不是完全延迟初始化,当该单例有其他static成员被使用时,该instance就被创建,而不是该instance被使用时才被创建。

(3)推荐方式:使用内部一个Instance的Holder来“持有”单例

复制代码
public sealed class Singleton
{
    private Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return Nested.instance;
        }
    }
    
    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly Singleton instance = new Singleton();
    }
}
复制代码

这种实现方式不需要加锁(without using locks)。

这种方式是完全延迟初始化(fully lazy instantiation),该Instance只有被使用时才被创建。

(4)最简单有效方式:

复制代码
/// <summary>
/// .NET 4's Lazy<T> type
/// </summary>
public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
}
复制代码

这种方式的简单和性能良好,而且还提供检查是否已经创建实例的属性IsValueCreated。

二、单例模式的Java实现:

(1)使用double-checked locking的方式:

复制代码
public class Singleton {

    private static volatile Singleton instance = null;

    private Singleton() { }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
复制代码

双重检验锁模式(double checked locking pattern), 是一种使用同步块加锁的方法。程序员称其为双重检查锁,因为会有两次检查 instance == null ,一次是在同步块外,一次是在同步块内。 为什么在同步块内还要再检验一次?因为可能会有多个线程一起进入同步块外的 if, 如果在同步块内不进行二次检验的话就会生成多个实例了。

注意:这里的volatile修饰是必不可少的,当一个变量被volatile修饰后, 它将具备两种特性:

1. 线程可见性:当一个线程修改了被volatile修饰的变量后,无论是否加锁,其它线程都可以立即看到最新的修改,而普通变量却做不到这点;

2. 禁止指令重排序优化:普通的变量仅仅保证在该方法的执行过程中所有依赖赋值结果的地方都能获取正确的结果,而不能保证变量赋值操作的顺序与程序代码的执行顺序一致。 

(2)使用Eager initialization的方式:

复制代码
public class Singleton 
{
    private static final Singleton instance = new Singleton();
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        return instance; 
    }
}
复制代码

(3)推荐实现:Initialization On Demand Holder Idiom

复制代码
public class Singleton 
{
    // Private constructor prevents instantiation from other classes
    private Singleton() { }
 
    /**
    * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
    * or the first access to SingletonHolder.INSTANCE, not before.
    */
    private static class SingletonHolder { 
        private static final Singleton instance = new Singleton();
    }
 
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}
复制代码

这种写法仍然使用JVM本身机制保证了线程安全问题,由于 SingletonHolder 是私有的,除了getInstance()之外没有办法访问它,因此它是Lazy Initialized的,同时读取实例的时候不会进行同步,没有性能缺陷,也不依赖 JDK 版本。

(4)最简单实现:使用一个enum来实现,线程安全性由JVM保证。

复制代码
public enum SingletonEnum {
    Instance;
    
    private SingletonEnum()
    {
        // Some initialize code
    }
}
复制代码

可以通过SingletonEnum.Instance来访问实例,这比调用getInstance()方法简单多了。 创建枚举默认就是线程安全的,所以不需要担心double checked locking, 而且还能防止反序列化导致重新创建新的对象。

 

参考:

http://en.wikipedia.org/wiki/Singleton_pattern

http://www.yoda.arachsys.com/csharp/singleton.html

http://www.cnblogs.com/rush/archive/2011/10/30/2229565.html

http://javarevisited.blogspot.gr/2012/07/why-enum-singleton-are-better-in-java.html

http://javarevisited.blogspot.sg/2011/03/10-interview-questions-on-singleton.html

 



    本文转自阿凡卢博客园博客,原文链接:http://www.cnblogs.com/luxiaoxun/p/3841223.html,如需转载请自行联系原作者

相关文章
|
存储 编译器 C语言
c语言中char的作用类型
c语言中char的作用类型
1126 0
|
安全 Linux
CentOS7下快速升级至OpenSSH9.4p1安全版本
CentOS7下快速升级至OpenSSH9.4p1安全版本
839 1
用dragstart、drag、dragend、dragover、drop、dragleave实现针对表格列的顺序进行拖拽排序(附带实现选择某几列数据显示或隐藏)
用dragstart、drag、dragend、dragover、drop、dragleave实现针对表格列的顺序进行拖拽排序(附带实现选择某几列数据显示或隐藏)
|
11月前
|
存储 缓存 监控
社交软件红包技术解密(四):微信红包系统是如何应对高并发的
本文将为读者介绍微信百亿级别红包背后的高并发设计实践,内容包括微信红包系统的技术难点、解决高并发问题通常使用的方案,以及微信红包系统的所采用高并发解决方案。
307 13
|
12月前
|
人工智能 自然语言处理 计算机视觉
OpenAI发布sCM提升50倍效率,扩散模型重大技术突破!
OpenAI近期发布了Simplified Consistency Models (sCM) 技术,这是在扩散模型基础上的重大改进,实现了50倍效率提升。sCM通过简化和稳定连续时间一致性模型的训练过程,解决了传统模型中的离散化误差和训练不稳定性问题,显著提升了生成模型的性能和效率。在多个数据集上的测试结果表明,sCM不仅超越了现有模型,还在生成模型的实际应用中展现了巨大潜力。论文地址:https://arxiv.org/abs/2410.11081
181 3
|
Java 数据库 开发者
"揭秘!SpringBoot+事务钩子,如何携手打造零差错、秒级响应的高效支付系统,让你的业务飞起来!"
【8月更文挑战第11天】构建高效稳定的支付系统时,Spring Boot凭借其快速开发与丰富生态成为优选框架。通过集成Spring事务管理抽象,@Transactional注解简化了数据库事务处理。针对复杂业务,可利用`TransactionSynchronizationManager`和`TransactionSynchronization`接口自定义事务钩子函数,在事务不同阶段执行特定逻辑,如支付成功或失败时的通知,确保数据一致性与业务完整性。
300 4
|
Web App开发 编解码 算法
浅谈语音质量保障:如何测试 RTC 中的音频质量?
日常音视频开会中我们或多或少会遭遇这些场景:“喂喂喂,可以听到我说话吗?我听你的声音断断续续的”,“咦,我怎么可以听到回声?”,“太吵啦,我听不清楚你在说啥” 等等。这些语音质量问题影响音视频开会体验,如若是重要的会议,那足够让人 “恼羞成怒”。那么如何有效的减少这些问题发生呢?本系列文章就将为大家分享阿里云视频云在保障 RTC 语音质量方面的测试经验。
浅谈语音质量保障:如何测试 RTC 中的音频质量?
|
缓存 负载均衡 应用服务中间件
【分布式技术专题】「分析Web服务器架构」Tomcat服务器的运行架构和LVS负载均衡的运行机制(修订版)
在本章内容中,我们将深入探讨 Tomcat 服务器的运行架构、LVS 负载均衡的运行机制以及 Cache 缓存机制,并提供相应的解决方案和指导。通过理解这些关键概念和机制,您将能够优化您的系统架构,提高性能和可扩展性。
494 4
【分布式技术专题】「分析Web服务器架构」Tomcat服务器的运行架构和LVS负载均衡的运行机制(修订版)
|
Cloud Native 测试技术 Nacos
云效AppStack+阿里云MSE实现应用服务全链路灰度
在应用开发测试验证通过后、进行生产发布前,为了降低新版本发布带来的风险,期望能够先部署到灰度环境,用小部分业务流量进行全链路灰度验证,验证通过后再全量发布生产。本文主要介绍如何通过阿里云MSE 微服务引擎和云效应用交付平台AppStack 实现灰度发布。
121913 3
|
Oracle 关系型数据库 数据挖掘
服务器数据恢复—raid5磁盘掉线热备盘未激活导致崩溃的数据恢复案例
服务器数据恢复环境: 某品牌X系列服务器,4块SAS硬盘组建了一组RAID5阵列,还有1块磁盘作为热备盘使用。服务器上层安装的linux操作系统,操作系统上部署了一个基于oracle数据库的OA(oracle已经不再为该OA系统提供后续服务支持)。 服务器故障: raid5中一块磁盘离线,热备盘未自动激活rebuild(原因不明)。服务器在运行一段时间后,另一块磁盘离线,RAID5阵列崩溃。用户方要求尽可能恢复服务器操作系统和服务器中的数据。
服务器数据恢复—raid5磁盘掉线热备盘未激活导致崩溃的数据恢复案例