设计模式——单例模式

简介:

  单例模式是一种常用的软件设计模式,其定义是单例对象的类 只允许一个实例存在。

许多时间我们系统只需要拥有一个全局对象,这样有利于我们协调系统的整体的行为。

单例模式(Singletion)

  保证一个类仅有一个实例,并提供一个访问该实例的全局访问点

基本实现思路:

  单例模式要求类能够有返回对象的一个接口 和 一个获得该实例的接口(静态方法)

①,该类的构造方法定义为私有方法,这样就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法得到该类唯一的实例对象。

②,该类内提供一个静态方法,当我们调用这个方法时,if类持有的接口不为空就返回这个接口所指向的内容,else创建该类的实例并用该接口指向该单例

对象注意事项:

  单例模式在多线程的应用场景下必须小心使用。会出现的问题以及如何解决下面会讲述。

单例模式的主要作用

  保证该对象的唯一性。同时严格控制客户端怎样访问该实例以及如何访问它。(唯一实例的受控访问)

懒汉式单例模式:第一次被引用的时间才会将类实例化。

情况1:下面代码是没有考虑多线程安全的普通代码的书写。

​​class Solution{

public:
    static Database* Get_Database()
    {
        if(mybase == NULL)
        {
            mybase = new Database;
        }
        return mybase;

    }
    

private:
    static Database* mybase;

}
Database* Solution:: mybase = NULL;//静态的类成员变量在类外初始化​​

分析:当pthread1进入到Get_database()函数的if判断内该线程的时间片刚好用完,此时pthread2调用Get_database()初始化了一个mybase。但是pthread1又获得了时间片,但是他不用判断mybase 是否 为NULL。此时就不是单例了(这样有可能会导致之前的数据丢失)。

优化:针对情况1,我们给初始化mybase时,加上锁的操作。

​​mutex mymutex;//定义一个全局的锁。

//将上述的Get_Database()修改为下面的

//加锁方法①
static Database* Get_Database()
{
    if(mybase == NULL)
    {
        mymutex.lock();
        mybase = new Database;
        mymutex.unlock();
    }
    return mybase;
}

//加锁方法②
static Database* Get_Database()
{
    mymutex.lock();
    if(mybase == NULL)
    {
        mybase = new Database;
    }
    mymutex.unlock();
    return mybase;
}
​​

分析:

第一种加锁方式中,其实和情况1出现的情况就是一样的。pthread1在if判断和加锁之间时间片到了,phtread2初始化了该数据库,此时pthread1便会继续new,导致上面的那个情况。

第二种加锁方式。该方式可以避免了上述线程不安全的问题。但是同时他又出现了另外一个问题:效率问题,我们设计一个项目不仅仅要求他们能够正常运行,还要考虑效率问题。这种方式加锁,导致多线程每次获取该数据库都会在获取锁时阻塞。这样就导致了时间效率的告诉下滑。

那么如何解决呢??上面的问题是出现在可能多个线程出现在第一个if判断和加锁之间,那么我们不妨在加一层判断,便可。

优化:

mutex mymutex;
static Get_Database()
{
    if(mybase == NULL)
    {   
        mymutex.lock();
        if(mybase == NULL)
        {
            mybase = new Database;
        }
        mymutex.unlock();
    }
    return mybase;
}
        
        

分析:这样就成功的避免了上述所说的线程不安全的问题。

饿汉式单例模式:只要类被加载的时间,就会将类实例化。

 

​​​class Solution{

public:
    static Database* Get_Database()
    {
        //直接返回一个实例化完成的对象
        return mybase;

    }
    

private:
    static Database* mybase;

    Solution(){   }//私有构造函数

}
Database* Solution:: mybase = new Database;//无论是否引用这类类对象,我们直接给他实例化。​​​

 

静态实例初始化在程序开始时进入主函数之前,就由主线程以单线程方式完成了初始化--饿汉式单例类,也就是静态初始化实例保证其线程安全性,故而在性能要求较高时,应该使用这种模式,避免繁琐的锁争夺。

饿汉式优点:写法比较简单,在类的装载的时间完成了实例化。避免了线程同步的问题。
饿汉式缺点:在类的装载的时候就完成了实例化,没有达到Lazy Loading的效果。如果从始至终都从未使用过这个实例,则会造成内存的浪费

 

 

 

 

 

 

拓展部分:

我们首先来看一下,new操作的步骤:①申请空间②构造该空间对象③将该空间的地址给mybase。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值