zoukankan      html  css  js  c++  java
  • [设计模式] 设计模式课程(十一)-- 单例模式

    概述

    • 属于“对象性能”模式
    • 面向对象很好地解决了“抽象”的问题,但不可避免地要付出一定的代价。对于通常情况来讲,面向对象的成本大都可忽略不计(继承),但某些特殊情况,面向对象带来的成本须谨慎处理(虚函数倍乘效应)
    • 动机:软件系统中有些特殊的类,必须确保它们在系统中只存在一个实例,才能确保逻辑正确及良好的效率
    • 确保一个类仅有一个实例,并提供一个该实例的全局访问点
    • 如何绕过常规构造器,提供一种机制,保证类只有一个实例
    • 类设计者的责任,而不是使用者的责任
    • 拷贝构造函数设计成私有(c++中,缺省构造函数和缺省拷贝构造函数都是共有的)
    • 高并发场景下,锁的代价过大
    • 双检查锁(锁前锁后都检查,防止两个进程前后脚进来),但内存读写reorder不安全,会导致双检查锁失效(线程在指令层次抢时间片,可能与代码顺序不一致)
    • new的默认顺序:分配内存--调用构造器--地址赋值给变量;实际优化后的可能顺序:分配内存--地址赋值给变量--调用构造器(导致线程B拿到了内存地址,而并没有执行构造器)
    • 解决:new过程不能reoder。Java:加volatile声明;C++atomic,memory_order_relaxed/release
    • Singleton模式中的实例构造器可设置为protected以允许子类派生
    • Singleton一般不支持拷贝构造函数和Clone接口,因为有可能导致多个对象实例,与模式初衷违背
    • 双锁检查正确实现,确保多线程环境下安全的Singleton

    场景

    • 一个党只有一个主席
    • Windows中,多个线程同时操作一个文件
    • 一个电脑有两台打印机,输出的时候不能两台打印机打印同一个文件
    • 要求生成唯一序列号
    • web中的计数器,不用每次刷新都在数据库里加一次,而是用单例缓存起来
    • 创建一个对象需要消耗的资源过多,如I/O与数据库的连接

    实现

    • 构造函数权限是private
    • 考虑线程安全
    • 考虑是否支持延迟加载
    • 考虑getInstance性能(是否加锁)
    • 饿汉式
      • 类加载时,instance静态实例已创建并初始化
      • 不支持延迟加载(等真正用到时再创建实例)
    • 懒汉式
      • 给getInstance()加锁
      • 支持延迟加载
      • 不支持高并发
    • 双重检测
      • IdGenerator 
      • 支持延迟加载和高并发
    • 静态内部类
      • 支持延迟加载和高并发
    • 枚举

    示例1(c++)

     1 class Singleton{
     2 private:
     3     Singleton();
     4     Singleton(const Singleton& other);
     5 public:
     6     static Singleton* getInstance();
     7     static Singleton* m_instance;
     8 };
     9 
    10 Singleton* Singleton::m_instance=nullptr;
    11 
    12 //线程非安全版本
    13 Singleton* Singleton::getInstance() {
    14     if (m_instance == nullptr) {
    15         m_instance = new Singleton();
    16     }
    17     return m_instance;
    18 }
    19 
    20 //线程安全版本,但锁的代价过高
    21 Singleton* Singleton::getInstance() {
    22     Lock lock;
    23     if (m_instance == nullptr) {
    24         m_instance = new Singleton();
    25     }
    26     return m_instance;
    27 }
    28 
    29 //双检查锁,但由于内存读写reorder不安全
    30 Singleton* Singleton::getInstance() {
    31     
    32     if(m_instance==nullptr){
    33         Lock lock;
    34         if (m_instance == nullptr) {
    35             m_instance = new Singleton();
    36         }
    37     }
    38     return m_instance;
    39 }
    40 
    41 //C++ 11版本之后的跨平台实现 (volatile)
    42 std::atomic<Singleton*> Singleton::m_instance;
    43 std::mutex Singleton::m_mutex;
    44 
    45 Singleton* Singleton::getInstance() {
    46     Singleton* tmp = m_instance.load(std::memory_order_relaxed);
    47     std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
    48     if (tmp == nullptr) {
    49         std::lock_guard<std::mutex> lock(m_mutex);
    50         tmp = m_instance.load(std::memory_order_relaxed);
    51         if (tmp == nullptr) {
    52             tmp = new Singleton;
    53             std::atomic_thread_fence(std::memory_order_release);//释放内存fence
    54             m_instance.store(tmp, std::memory_order_relaxed);
    55         }
    56     }
    57     return tmp;
    58 }
    View Code

    示例2(java)

     1 public class SingletonLazy {
     2     public static void main(String[] args) {
     3         President zt1 = President.getInstance();
     4         zt1.getName();
     5         President zt2 = President.getInstance();
     6         zt2.getName();
     7         if(zt1==zt2) {
     8             System.out.println("他们是同一人!");
     9         }else {
    10             System.out.println("他们不是同一人!");
    11         }
    12     }
    13 }
    14 
    15 class President{
    16     private static volatile President instance = null;
    17     
    18     private President() {
    19             System.out.println("产生一个总统!");
    20     }
    21     
    22     public static synchronized President getInstance() {
    23         if(instance==null) {    
    24             instance=new President();
    25         }
    26         else {
    27             System.out.println("已经有一个总统,不能产生新总统!");
    28         }
    29         return instance;
    30     }
    31     
    32     public void getName() {
    33         System.out.println("我是美国总统:特朗普");
    34     }
    35 }
    View Code
  • 相关阅读:
    HDU 2888 Check Corners (模板题)【二维RMQ】
    POJ 3264 Balanced Lineup(模板题)【RMQ】
    poj 3368 Frequent values(经典)【RMQ】
    SPOJ RPLN (模板题)(ST算法)【RMQ】
    UVA 796 Critical Links(模板题)(无向图求桥)
    UVA 315 Network (模板题)(无向图求割点)
    POJ 2029 Get Many Persimmon Trees (模板题)【二维树状数组】
    poj 3067 Japan 【树状数组】
    POJ 2481 Cows 【树状数组】
    POJ 1195 Mobile phones【二维树状数组】
  • 原文地址:https://www.cnblogs.com/cxc1357/p/12310649.html
Copyright © 2011-2022 走看看