单例模式是最简单的设计模式之一,属于创建型模式。涉及到创建一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。
特点:
单例类只能有一个实例
单例类必须自己创建自己的唯一实例
单例类必须给所有其他对象提供这一实例
总的来说就是用于一种特定情况下的类定义方式,主要用于只需要一个实例的时候,比如产生唯一的序列号,web中的计时器等。类需提供全局的接口用来获取实例。
构造函数是私有的,这个很好理解,私有的构造函数可以防止外部重复实例化,因为只要构造函数调用成功就会分配到内存空间。
单例模式的实现方式有几种
线程不安全
java版
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
简单的几行代码就把最简单的单例模式写出来了,原理很简单,就是自身包含一个自身的引用,并且存储类型是static的,保证不会因为作用域消失而销毁。构造函数中不需要写实现,定义成private只是为了防止外部重复实例化。而得到这个唯一的对象的方式就是通过调用方法,方法也是static的,也必须是static的,方法内部判断如果引用为空则分配空间并返回,若引用不为空则直接返回。这个对象的引用明显在外部不可改变。
C++版
class Singleton{
public:
static Singleton *getlnstance()
{
if(instance == NULL){
instance = new Singleton();
}
return instance;
}
private:
Singleton(){}
static Singleton *instance;
};
Singleton * Singleton::instance = NULL;
基本就是这样,当然这只是个模型,并不能作为我们应用在项目中,因为这是线程不安全的,为什么说线程不安全,就是说当一个线程抢占到资源后,在分配空间的时候可能不会完成这个操作,如果没有完成,那么这个引用虽然不为空,但是空间是不完整的,另一个线程抢占到资源访问内部成员就会出现段错误。
线程安全
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
乍看上去和上面的代码没有什么区别,其实确实差别不大,唯一的区别是加入了同步,不过一般java中说的同步其实是操作系统中的互斥。synchronized关键字,保证在多线程程序中防止多处同时调用函数,比如一个new操作创建一半时间片结束,另一个线程使用这个对象。但是对象并没有创建完成,会造成错误甚至内存崩溃。
c++版
class Singleton{
public:
static Singleton *getInstance()
{
mutex.lock();
if(instance == NULL){
instance = new Singleton();
}
mutex.unlock();
return instance;
}
private:
Singleton(){}
static Singleton *instance;
};
Singleton Singleton::instance = NULL;
c++中没有像java中的同步关键字实现同步,但是可以使用锁将代码段加锁,保证在多线程的情况下保证数据的安全性。
java中常用的线程安全单例模式
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
这里没有进行加锁的操作,但是依然可以做到线程安全,原理是因为在类载入虚拟机的时候时就已经进行了初始化,不会出现只构造一半没有成功就被使用的情况。
c++版
class SingleTon
{
public:
static SingleTon* getInstance()
{
return &singleTon;
}
private:
SingleTon(){}
static SingleTon singleTon;
};
SingleTon SingleTon::singleTon;
在c++的快加载模式中使用了具体对象而非指针的方式,空间分配时任何代码还没有开始执行,所以说是线程安全的。