设计模式8-单件模式Singleton

单件模式属于 对象性能 模式
面向对象很好的解决了抽象问题,但是必不可免的要付出一定的代价.对于通常的情况来讲,面向对象的成本可以忽略不计.但是在某些情况下,面向对象带来的成本必须谨慎处理.
典型模式:

  • Singleton
  • Flyweight

动机

在软件系统中,经常有一些特殊的类,必须保证他们只存在一个实例,才能保证逻辑的正确性,以及良好的效率.
保证只存在一个类的实例,应该是类设计者的责任,而不是类使用者的责任.

实例

首先应该将类的构造函数和拷贝构造函数设置成私有的,利用静态成员函数来访问类的唯一实例.

class Singleton {
 public:
    static Singleton* GetInstance();
    static Singleton* instance;    
 private:
    Singleton();
    Singleton(const Singleton& other);
};

Singleton*  Singleton::instance = nullptr;

静态函数GetInstance成员函数的创建还是很有学问的.

首先看第一种实现:

//线程非安全版本
Singleton*  Singleton::GetInstance() {
    if (instance == nullptr) {
        instance =  new Singleton();
    }
    return instance;
}

这个方式在单线程的条件是可以的,但是在多线程的情况下会出问题,假如两个线程同时进入了 if(instance == nullptr) 这个时候两个线程都会执行instance = new Singleton()产生了两个对象.

第二个版本

//线程安全版本,但是锁的代价过高
Singleton*  Singleton::GetInstance() {
    Lock lock;
    if (instance==nullptr) {
        instance =  new Singleton();
    }
    return instance;
}

该版本线程是安全的,可以保证,只会new一次Singleton.但是某一个线程执行return instance的操作,也需要等待其他线程return才能进行,而在变量的读操作是不需要加锁的,变量的写操作才需要加锁.在高并发的环境下,例如10w人同时对一个网页进行访问,这个加锁的代价是很高的.

第三种实现

//双检查锁,但由于内存读写reorder 不安全
Singleton*  Singleton::GetInstance() {    
    if (instance == nullptr) {
        Lock lock;
        if (instance == nullptr) {

            instance =  new Singleton();
        }
    }
    return instance;
}

这种实现,看起来很完美,但是有漏洞.对于instance = new Singleton();指令,我们理想的执行顺序是,1)分配内存,2)执行Singleton的构造函数,3)将分配存储空间的地址赋值给instance.但是编译器可能出于优化的考虑,真实的执行顺序为1)分配内存,2)将分配存储空间的地址赋值给instance,3)执行Singleton的构造函数.这种情况下,可能出现,instance不为空指针,但是还没有初始化完成,其它线程看到这个指针不为空,就拿这个指针去用了,导致不安全的情况.

解决这个问题,其实还是比较麻烦的

//c++ 11 版本之后的跨平台实现

std::atomic<Singleton* > Singleton::instance;
std::mutex Singleton::mutex;

Singleton*  GetInstance() {
    Singleton* tem = instance.load(std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
    if (tem == nullptr) {
        std::lock_guard<std::mutex> lock(mutex);
        tmp = instance.load(std::memory_order_relaxed);
        if (tem == nullptr) {
            tem = new Singleton();
            std::atomic_thread_fence(std::memory_order_release);//释放内存fence
            instance.store(tem,std::memory_order_relaxed);
        }
    }
    return tem;

}

单例模式的定义,保证一个类有且仅有一个实例,并提供一个该实例的全局访问点.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java设计模式是一组经过实践验证的面向对象设计原则和模式,可以帮助开发人员解决常见的软件设计问题。下面是常见的23种设计模式: 1. 创建型模式(Creational Patterns): - 工厂方法模式(Factory Method Pattern) - 抽象工厂模式(Abstract Factory Pattern) - 单例模式Singleton Pattern) - 原型模式(Prototype Pattern) - 建造者模式(Builder Pattern) 2. 结构型模式(Structural Patterns): - 适配器模式(Adapter Pattern) - 桥接模式(Bridge Pattern) - 组合模式(Composite Pattern) - 装饰器模式(Decorator Pattern) - 外观模式(Facade Pattern) - 享元模式(Flyweight Pattern) - 代理模式(Proxy Pattern) 3. 行为型模式(Behavioral Patterns): - 责任链模式(Chain of Responsibility Pattern) - 命令模式(Command Pattern) - 解释器模式(Interpreter Pattern) - 迭代器模式(Iterator Pattern) - 中介者模式(Mediator Pattern) - 备忘录模式(Memento Pattern) - 观察者模式(Observer Pattern) - 状态模式(State Pattern) - 策略模式(Strategy Pattern) - 模板方法模式(Template Method Pattern) - 访问者模式(Visitor Pattern) 4. 并发型模式(Concurrency Patterns): - 保护性暂停模式(Guarded Suspension Pattern) - 生产者-消费者模式(Producer-Consumer Pattern) - 读写锁模式(Read-Write Lock Pattern) - 信号量模式(Semaphore Pattern) - 线程池模式(Thread Pool Pattern) 这些设计模式可以根据问题的特点和需求来选择使用,它们提供了一些可复用的解决方案,有助于开发高质量、可维护且易于扩展的软件系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值