优点:比直接加锁效率高 。
缺点:内存读写reorder不安全 。
reorder问题:一般new的执行过程认为是分配内存->构造函数初始化->返回地址,但是实际上可能是分配内存->返回地址->构造函数这种错乱的顺序 。如果线程1reorder,另外一个线程2判断非空直接返回pSingleton,但是这个对象实例是无法正常使用的,它只是一个还未调用构造函数初始化的内存 。
所以编译器需要解决这类问题,即编译器不能优化 。
3)volatile
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;
Singleton* Singleton::getInstance() {
Singleton* tmp = m_instance.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acuire);//获取内存fence
if (nullptr == tmp) {
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release);//释放内存fence
m_instance.store(tmp,std::memory_order_relaxed);
}
return tmp;
}
volatile这个关键字有两层语义:
第一层语义是可见性 。可见性指的是在一个线程中对该变量的修改会马上由工作内存(Work Memory)写回主内存(Main Memory),所以会马上反应在其它线程的读取操作中,即看到的都是最新的结果 。
第二层语义是禁止指令重排序优化 。我们写的代码(尤其是多线程代码),由于编译器优化,在实际执行的时候可能与我们编写的顺序不同 。
3.3 静态内部类
有没有一种延时加载,并且能保证线程安全的简单写法呢?我们可以把Singleton实例放到一个静态内部类中,这样就避免了静态实例在Singleton类加载的时候就创建对象,并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的:
public class Singleton {
private static class Holder {
private static Singleton singleton = new Singleton();
}
private Singleton(){}
public static Singleton getSingleton(){
return Holder.singleton;//静态类方式获取实例
}
}
3.4 选择
单例模式懒汉模式饿汉模式概念在类加载时不创建实例,采用延迟加载的方式,在运行调用时创建实例在类加载的时候,就完成初始化特点类加载速度快,但是运行时获取对象的速度较慢(时间换空间) 。类加载较慢,但获取对象速度快(空间换时间) 。延迟加载具备不具备线程安全线程不安全线程安全一般采用饿汉式,若对资源十分在意可以采用静态内部类,不建议采用懒汉式及双重检测 。
04特点4.1 优点1、提供了对唯一实例的受控访问 。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它 。
2、只存在一个对象,节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能 。
4.2 缺点1、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难 。
2、单例类的职责过重,在一定程度上违背了“单一职责原则” 。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起 。
3、滥用单例将带来一些负面问题 。现在很多面向对象语言(如Java)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失 。
05应用场景在以下情况下可以使用单例模式:1、系统只需要一个实例对象(线程池、缓存、硬件设备等),或者需要考虑资源消耗太大而只允许创建一个对象 。
推荐阅读
- 布隆过滤器原理及应用
- 雷凌有手动模式吗
- iphone11的飞行模式在哪里
- iphone11拍照夜间模式怎么关闭
- g5bootloader模式,韩版g5 root
- 奔驰g500怎样切换高速模式 奔驰g500怎么切换模式
- 苹果11pro夜景模式在哪里
- 三星盖世2用刷机精灵刷机后卡recovery模式
- 手机壳图片大全 可爱 背景图片 vivo手机壳图片大全
- 6大智慧功能值得一试,大智慧手机版本背景白