单例模式
时间:2019-10-19
本文章向大家介绍单例模式,主要包括单例模式使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
单例模式:一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
要定义一个单例模式的类,首先将其构造函数私有化,以防止外界通过其创建对象。因为单例类仅有一个实例,所以创建一个类的私有静态指针变量,使其指向类的唯一实例。要使外界能够获取到私有静态指针变量指向的实例,所以要创建一个公有的静态函数。
1、懒汉版
1 class Singleton 2 { 3 private: 4 //构造函数私有化 5 Singleton() {}; 6 ~Singleton() {}; 7 Singleton(const Singleton&); 8 Singleton& operator=(const Singleton&); 9 10 private: 11 //指向唯一实例的私有静态指针变量 12 static Singleton *instance; 13 14 public: 15 //公有静态函数 16 static Singleton* getInstance() 17 { 18 if (instance == NULL) 19 { 20 instance = new Singleton(); 21 } 22 return instance; 23 } 24 }; 25 26 Singleton *Singleton::instance = NULL;
这个版本在单线程下是没有问题的,但是如果是多线程使用的话是不安全的。假设有两个线程调用getInstance(),instance=NULL,1线程执行到20行代码,此时2线程获的时间片,2线程同样会执行20行代码,这样就造成了线程的不安全。
2、加锁版
class Singleton { private: //构造函数私有化 Singleton() {}; ~Singleton() {}; Singleton(const Singleton&); Singleton& operator=(const Singleton&); private: //指向唯一实例的私有静态指针变量 static Singleton *instance; public: //公有静态函数 static Singleton* getInstance() { Lock lock; if (instance == NULL) { instance = new Singleton(); } return instance; } }; Singleton *Singleton::instance = NULL;
这样的是线程安全的,但是锁的使用会降低性能,如果是web高并发下,这种版本效率太低。
3、双检查锁版
1 class Singleton 2 { 3 private: 4 //构造函数私有化 5 Singleton() {}; 6 ~Singleton() {}; 7 Singleton(const Singleton&); 8 Singleton& operator=(const Singleton&); 9 10 private: 11 //指向唯一实例的私有静态指针变量 12 static Singleton *instance; 13 14 public: 15 //公有静态函数 16 static Singleton* getInstance() 17 { 18 if(instance == NULL) 19 { 20 //基于作用域加锁 21 Lock lock; 22 if (instance == NULL) 23 { 24 instance = new Singleton(); 25 } 26 }//超出作用域解锁 27 return instance; 28 } 29 }; 30 31 Singleton *Singleton::instance = NULL;
这个版本下,按前面所假设情况,2线程会阻塞在21行,直到1线程执行完毕,但是这个版本还是有问题。在24行,我们通常所认为的指令执行顺序应该是:先创建内存,然后构造器构造,最后将内存地址返回,但是很多编译器会对程序进行优化,优化后的指令执行顺序有可能会是:先创建内存,然后将内存地址返回,最后构造器构造。这样的话有可能2线程在18行判断instance!=NULL,直接返回instance,然后直接使用instance指向的内存中的数据,但是内存中可能没有数据,此时程序就出现了错误。
解决办法:
1 class Singleton 2 { 3 private: 4 //构造函数私有化 5 Singleton() {}; 6 ~Singleton() {}; 7 Singleton(const Singleton&); 8 Singleton& operator=(const Singleton&); 9 10 private: 11 //指向唯一实例的私有静态指针变量 12 static std::atomic<Singleton *> m_instance; 13 static std::mutex m_mutex; 14 15 public: 16 //公有静态函数 17 static Singleton* getInstance() 18 { 19 Singleton *tmp= m_instance.load(std::memory_order_relaxed); 20 //获取内存fence,tmp不被编译器优化 21 std::atomic_thread_fence(std::memory_order_acquire); 22 if(tmp==nullptr) 23 { 24 std::lock_guard<std::mutex> lock(m_mutex); 25 tmp= m_instance.load(std::memory_order_relaxed); 26 if(tmp==nullptr) 27 { 28 tmp= new Singleton; 29 //释放fence 30 std::atomic_thread_fence(std::memory_order_release); 31 m_instance.store(tmp, std::memory_order_relaxed); 32 } 33 } 34 return tmp; 35 } 36 };
原文地址:https://www.cnblogs.com/qiu00/p/11701331.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法