设计模式学习--单例模式
2016-06-27 22:32
489 查看
在设计模式中,单例模式是最常见的一种模式。下面是本人学习单例模式的一些总结。用java和C++两种语言实现单例模式
下面就给出Java和C++两种具体语言的实现。
因为JVM的重排序,会导致未被初始化的实例.
但这种方式,在C++0x之前,是线程不安全的,C++0x之后,局部静态变量是线程安全的。
这种实现,有个问题,单例的初始化,是类的静态初始化时完成的,无法控制两个类的初始化顺序。如果有两个单例相互依赖,则会有问题。
利用C++的模板和friend友元功能,可以快速产生单例类
定义
确保一个类只有一个实例,并提供一个全局访问点。实现方法
如何在实际软件中,做到类只有一个实例呢。最简单的方法,就是使类的构造函数私有,这样类的使用者,就不能创建类的实例,然后,再提供统一的获取类实例的方法。下面就给出Java和C++两种具体语言的实现。
JAVA实现方法
静态变量
Singleton最简单的方法,就是直接声明一个static变量,并且进行初始化,这样类在加载的时候,就会初始化实例。public class Singleton { private volatile static Singleton singleton = new Singleton(); private Singleton() { } public static Singleton getInstance() { return singleton; } }
双检锁
使用双检索的方式,必须添加volatile关键字,因为JVM的重排序,会导致未被初始化的实例.
public class Singleton { private volatile static Singleton singleton = null; private Singleton(){} public static Singleton getInstance(){ if (singleton== null) { synchronized (Singleton.class) { if (singleton== null) { //没有volatile关键字会因为重排序,导致singleton!=null,而实例未被成功初始化 singleton= new Singleton(); } } } return singleton; } }
enum实现
使用jdk1.5之后的enum可以很方便实现单例。public enum Singleton { INSTANCE; }
C++实现方法
C++和java差不多,但C++中的构造函数种类比较多,有普通构造函数,有拷贝构造函数。为了避免实例被外部delete,可以将类的析构函数也私有,这样在外部调用delete的时候,也会有编译错误。静态局部变量
C++最简单的方式,就是使用静态局部变量。在第一次获取instance的时候,会进行初始化。#include <iostream> using namespace std; class Singleton{ public: static Singleton& getInstance(){ static Singleton instance; return instance; } void print(){ cout << "Hello, my data:" << data << endl; } void add(int i){ data += i; } private: Singleton(){ data = 0; }; Singleton(Singleton &){} ~Singleton(){} int data; }; int main(int argc, const char* argv[]) { Singleton & s1 = Singleton::getInstance(); Singleton & s2 = Singleton::getInstance(); s1.print(); s2.add(2); s1.print(); }
但这种方式,在C++0x之前,是线程不安全的,C++0x之后,局部静态变量是线程安全的。
静态变量
C++中,为了避免C++中局部变量的线程安全问题,可以将局部静态变量放到类的静态变量中。#include <iostream> using namespace std; class Singleton{ public: static Singleton& getInstance(){ return instance; } void print(){ cout << "Hello, my data:" << data << endl; } void add(int i){ data += i; } private: static Singleton instance; private: Singleton(){ data = 0; }; Singleton(Singleton &){} ~Singleton(){} int data; }; Singleton Singleton::instance ; int main() { Singleton & s1 = Singleton::getInstance(); Singleton & s2 = Singleton::getInstance(); s1.print(); s2.add(2); s1.print(); return 0; }
这种实现,有个问题,单例的初始化,是类的静态初始化时完成的,无法控制两个类的初始化顺序。如果有两个单例相互依赖,则会有问题。
双检锁
为了使用延迟初始化,同时解决线程安全问题,可以使用双检锁的方式,先判断实例是否为空,如果为空,则加锁,加锁后,再进行一次为空判断。这样,只有在单例被初始化的时候,才会有加锁的性能问题。#include <iostream> #include <mutex> using namespace std; class Singleton{ public: static Singleton& getInstance(){ if (instance == NULL){ m.lock(); if (instance == NULL){ instance = new Singleton(); } m.unlock(); } return *instance; } void print(){ cout << "Hello, my data:" << data << endl; } void add(int i){ data += i; } private: static Singleton* instance;// = new Singleton(); static mutex m; private: Singleton(){ data = 0; }; Singleton(Singleton &){} ~Singleton(){} int data; }; Singleton* Singleton::instance = NULL; mutex Singleton::m; int main(int argc, const char* argv[]) { Singleton & s1 = Singleton::getInstance(); Singleton & s2 = Singleton::getInstance(); s1.print(); s2.add(2); s1.print(); return 0; }
单例角
如果有多个单例,都想使用双检锁的方法,利用C++的模板和friend友元功能,可以快速产生单例类
#include <iostream> #include <mutex> using namespace std; template<class T> class Singleton{ public: static T& getInstance(){ if (nullptr == instance){ m.lock(); if (nullptr == instance){ instance = new T(); } m.unlock(); } return *instance; } private: static T* instance; static mutex m; protected: Singleton(){}; Singleton(Singleton &){} ~Singleton(){} }; template<class T> T* Singleton<T>::instance = nullptr; template<class T> mutex Singleton<T>::m; class MyClass : public Singleton<MyClass>{ friend class Singleton<MyClass>; public: void print(){ cout << "Hello, my data:" << data << endl; } void add(int i){ data += i; } private: MyClass():data(0){} MyClass(MyClass &){} ~MyClass(){} int data; }; int main(int argc, const char* argv[]) { MyClass & s1 = MyClass::getInstance(); MyClass & s2 = MyClass::getInstance(); s1.print(); s2.add(2); s1.print(); return 0; }
应用场景
在现实中,有很多地方都是需要用到单例模式的,比如全局配置信息,数据库连接池相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 关于指针的一些事情