初窥设计模式——单例模式

资料借鉴:http://cantellow.iteye.com/blog/838473

目       录

简短介绍:

季回           设备驱动管理器的设计… 2

  单例模式是一律种植常下的软件设计模式。在其的为主结构面临仅仅包含一个被叫做单例的异类。通过单例模式可以保证系统中应用该模式之类就来一个实例。即一个好像才发一个实例

4.1           接口定义… 2

定义:

4.2           设备容器… 7

  一个接近闹还只有发一个实例,并且自动实例化向全体系提供。

4.3           生成设备ID.. 7

特点:

4.4           对设备容器操作的互斥… 8

       1、单例类只能有一个实例。

4.5           获得装备列表… 8

  2、单例类必须自己创造自己之唯一实例。

4.6           设备计数器的与众不同用处… 8

  3、单例类必须叫有其他对象提供这无异于实例。

4.7           小结… 10

落实方式:

 

  一般的话,我们发出以下几只必备之操作:

季段     设备驱动管理器的规划

   
设备驱动管理器是本着IRunDevice设备驱动接口的保管,是框架重要之一对之一。不管设备驱动管理器怎么统筹、以什么形式存在,在概念上一定是存的。设计好之设备驱动管理器对于框架平台的康乐运行重大。

  
于介绍装备驱动管理器之前,先简单介绍一下IO控制器(IIOController),它要负责对IO和设备开展调度,并让装置运转,在《第5节
串口和网络的IO设计》进行详尽的牵线。也就是说一个IO控制器可能会见指向许多个装备驱动(插件)。

  
早期的时,每个IO控制器都来一个配备驱动管理器。在框架平台启动之时段,根据设备驱动的报道参数将相应的装置驱动分配到相应的IO管理器;当IO参数有转移的当儿,会硌事件,把欠装备驱动从当下IO控制器移动至另外一个IO控制器。

  
从工作角度来考虑,这样做并从未呀问题,并且一直运行的挺平静。但是,从模块化、扩展性角度来设想,不是太出色,如果当另外地方调用某一个装置驱动时,不能够直接、很快的找到该设施驱动,必要遍历IO控制器又配合相应的设备驱动,并且操作麻烦以及效率不高。

  
在针对框架平台拓展重构的时节,把欠问题展开了重新考虑,并把彼此关联的题目一并化解。把每个IO控制器中的装置驱动管理器进行了成,用一个装备驱动管理器来形成框架平台的调和工作。

  
这块涉及到的技艺并无为难,也大容易了解,但是于计划过程中需小心有细节问题,这些问题也许影响框架平台的祥和。

    1、私有化构造方法;

4.1    接口定义

    先定义一个接口(IDeviceManager<TKey,
TValue>),确定设备驱动管理器都如水到渠成什么力量,增加设备、删除设备、获得装备及列表、以及任何的职能。接口代码如下:

public interface IDeviceManager<TKey, TValue> : IEnumerable<TValue> where TValue : IRunDevice
{
       /// <summary>
       /// 新建设备的ID,且唯一
       /// </summary>
       /// <returns></returns>
       string BuildDeviceID();

       /// <summary>
       /// 增加设备
       /// </summary>
       /// <param name="key"></param>
       /// <param name="val"></param>
       void AddDevice(TKey key, TValue val);

       /// <summary>
       /// 删除设备
       /// </summary>
       /// <param name="key"></param>
       void RemoveDevice(TKey key);

       /// <summary>
       /// 移除所有设备
       /// </summary>
       void RemoveAllDevice();

       /// <summary>
       /// 获得值集合
       /// </summary>
       /// <returns></returns>
       List<TValue> GetValues();

       /// <summary>
       /// 获得关键字集合
       /// </summary>
       /// <returns></returns>
       List<TKey> GetKeys();

       /// <summary>
       /// 获得设备的ID和名称
       /// </summary>
       /// <returns></returns>
       Dictionary<int, string> GetDeviceIDAndName();

       /// <summary>
       /// 获得高优先运行设备
       /// </summary>
       /// <param name="vals"></param>
       /// <returns></returns>
       TValue GetPriorityDevice(TValue[] vals);

       /// <summary>
       /// 获得单个设备
       /// </summary>
       /// <param name="key"></param>
       /// <returns></returns>
       TValue GetDevice(TKey key);

       /// <summary>
       /// 获得设备数组
       /// </summary>
       /// <param name="para">IP或串口号</param>
       /// <param name="ioType">通讯类型</param>
       /// <returns></returns>
       TValue[] GetDevices(string para, CommunicationType ioType);

       /// <summary>
       /// 获得指定IP和工作模式的网络设备
       /// </summary>
       /// <param name="remoteIP"></param>
       /// <param name="workMode"></param>
       /// <returns></returns>
       TValue[] GetDevices(string remoteIP, WorkMode workMode);

       /// <summary>
       /// 获得指定工作模式的网络设备
       /// </summary>
       /// <param name="workMode"></param>
       /// <returns></returns>
       TValue[] GetDevices(WorkMode workMode);

       /// <summary>
       /// 获得设备数组
       /// </summary>
       /// <param name="ioType"></param>
       /// <returns></returns>
       TValue[] GetDevices(CommunicationType ioType);

       /// <summary>
       /// 按设备类型获得设备
       /// </summary>
       /// <param name="devType"></param>
       /// <returns></returns>
       TValue[] GetDevices(Device.DeviceType devType);

       /// <summary>
       /// 判断设备是否存在
       /// </summary>
       /// <param name="key"></param>
       /// <returns></returns>
       bool ContainDevice(TKey key);

       /// <summary>
       /// 根据输入参数,判断是否包括设备
       /// </summary>
       /// <param name="para">IP或串口号</param>
       /// <param name="ioType">设备通讯类型</param>
       /// <returns></returns>
       bool ContainDevice(string para, CommunicationType ioType);

       /// <summary>
       /// 设置用户级别
       /// </summary>
       /// <param name="userlevel"></param>
       void SetUserLevel(UserLevel userlevel);

       /// <summary>
      /// 设置是否注册
       /// </summary>
       /// <param name="isreg"></param>
       void SetIsRegLicense(bool isreg);

       /// <summary>
       /// 获得可用设备数
       /// </summary>
       int Count { get; }

       /// <summary>
       /// 获得设备的计数器的值
       /// </summary>
       /// <param name="key"></param>
       ///<returns></returns>
       int GetCounter(TKey key);

       /// <summary>
       /// 设置计数器的值
       /// </summary>
       /// <param name="key"></param>
       /// <param name="val"></param>
       void SetCounter(TKey key, int val);
}

 4.2    设备容器

  
设备驱动管理器是针对性Dictionary<Key,Value>的包裹,Key是设备驱动的ID,Value是IRunDevice设备驱动接口。设备驱动管理器需要跨线程应用,所以针对Dictionary操作而加线程同步锁。

   当时利用的凡.NET Framework 2.0框架,没有ConcurrentDictionary(Of TKey,
TValue)字典类,这个近乎的所有国有和于保障的成员还是线程安全之,使用原子性操作,适合多只线程之间以使用。再重复构时可以下ConcurrentDictionary类代替Dictionary类,因为ConcurrentDictionary的有所操作使用到了Monitor线程同步类,不欲团结重新展开打包。

   不粘ConcurrentDictionary类的源代码了,具体应用参考MSDN。

    2、final类(定义为不可持续,这点开及无涉嫌,暂时还当研讨)

4.3    生成设备ID

    查寻设备驱动管理器中尽可怜之设施ID,并在这个基础及加1。这块代码很粗略,

如下:

public string BuildDeviceID()
{
       if(_dic.Count>0)
       {
          int maxID=_dic.Max(d => d.Value.DeviceParameter.DeviceID);
          return (++maxID);
       }
       else
       {
              return 0;
       }
}

   
增加设备驱动是索要转变设备ID,一般采用手动增加设备驱动,所以于这块不待加线程同步锁。

    3、将类似的实例对象定义为一个私有的属性(不克为成员变量)

4.4    对配备容器操作的排挤

框架平台有组件要共享设备驱动管理器,所以会涉嫌到跨线程应用,特别

是当集合有转移之早晚,可能会见出现异常。例如:启动框架平台的时光,IO控制器已经起步,IO控制器从设备驱动管理器提取自己的装备列表,但是此时有或还没加载了设备驱动,当有新的配备驱动增加到装备驱动管理时,可能会见掀起冲突。

   
所以,在添设备、删除设备与落装备列表的时段多了线程同步锁,例如:lock
(_SyncLock)。

    4、通过getInstance()方法赢得实例,若私有的实例属性对象引用不为空则返回,否则实例化该属性并回

4.5    获得装备列表

有差不多独得到装备的构造函数(GetDevices),主要是满足不同的用场景。

伸手参见“4.1接口定义”。

   
另外,获得高优先运行设备的GetPriorityDevice函数在上一章节都介绍了。

  这里先介绍四种实现方式

4.6    设备计数器的异样用途

    在接口定义中发出SetCounter和GetCounter两只函数,用在通讯过程遭到。

   
应用场景是这般的,在起和收通讯模式中,设备驱动一直处于在通讯正常的情状下,但是忽然发出线路中断或外原因促成无法吸收及多少时,那么设备驱动一直无法接受至数,也束手无策对报道状态进行检测和反相应的数量信息,也就是说现实情况已经闹改变,但是设备驱动却无计可施得到响应。

   
为了以防万一这种情形的起,设备驱动每次发送数据时,通过GetCounter函数获得当前设施驱动之计数器,对计数器(变量)+1操作,并经SetCounter函数把计数器(变量)再写及装备驱动管理器中。在好接收数据的时节,执行同一之流水线,但是执行-1操作。如果直白发送数据,而从未接受到数量常常,当前配备驱动的计数器就会直接以增长。如果过量等于有值的时段,就会由此RunIODevice(new
byte[]{})驱动当前设备,执行总体设施处理流程,二次开发的代码块就会受调用,来成功此类应用场景的状态改变跟多少变化。代码如下:

int counter = DeviceManager.GetInstance().GetCounter(dev.DeviceParameter.DeviceID.ToString());
int sendNum = SessionSocketManager.GetInstance().Send(dev.DeviceParameter.NET.RemoteIP, data);
if (sendNum == data.Length && sendNum != 0)
{
       DeviceMonitorLog.WriteLog(dev.DeviceParameter.DeviceName, "发送请求数据");
       Interlocked.Increment(ref counter);
}
else
{
       Interlocked.Increment(ref counter);
       DeviceMonitorLog.WriteLog(dev.DeviceParameter.DeviceName, "尝试发送数据失败");
}
dev.ShowMonitorIOData(data, "发送");
if (counter >= 3)
{
       try
       {
              dev.RunIODevice(new byte[] { });
       }
       catch (Exception ex)
       {
              DeviceMonitorLog.WriteLog(dev.DeviceParameter.DeviceName, ex.Message);
              GeneralLog.WriteLog(ex);
       }
       Interlocked.Exchange(ref counter, 0);
}
DeviceManager.GetInstance().SetCounter(dev.DeviceParameter.DeviceID.ToString(), counter);

  
对于发送和接收数据会以不同之线程上做到,在针对计数器(变量)进行+1和-1操作的时用及了Interlocked类,用于多单线程共享的变量提供原子操作,防止在多处理器上并行操作时或许引发的不得了或者数中损坏。

      1、饿汉模式

4.7    小结

  
这样改造后,不仅可以于IO控制器对配备进行引用,也堪以任何零件使用。如果遇类似的景象,希望用ConcurrentDictionary类。

 

作者:唯笑志在

Email:504547114@qq.com

QQ:504547114

.NET开发技术联盟:54256083

文档下载:http://pan.baidu.com/s/1pJ7lZWf

合法网址:http://www.bmpj.net

    2、懒汉模式

    3、双重认证

    4、静态内部类模式

 

事先看率先栽方式,饿汉模式顾名思义,迫不及待的眷恋要吃(初始化实例),在看似吃定义一个个体静态本类的实例化对象,在相近加载的经过尽管进展这目标的实例化,之后的指向该类实例的调用都是调动用是实例。

代码如下:

public class Singleton2 {

    private static Singleton2 singleton2= new Singleton2();

    private Singleton2(){}

    public static Singleton2 getInstance() {
        return singleton2;
    }

    public static String getStr() {
        return "Create String type Object";
    }
}

饿汉模式是较为简单的落实方式,同样为是较常用之方式。但他有所必然之欠缺:虽然以单例模式被几近还只调用getInstance()方法,但未排除来另的法门导致类似加载,比如要类似吃getStr()这种与类似的实例无关的法要为调用,就见面触发类加载,从而对静态成员开展初始化,但是此类有或并不需要实例化,这样在某种程度上会促成一定的资源浪费。也就是无法达到lazy loading的效果。

 

旋即虽引入了懒汉模式

 

懒汉模式:只有以第一此调用类的实例对象时才见面初始化类的实例,从而实现延迟加载

代码如下

public class Singleton3 {

    private static Singleton3 singleton2 = null;

    private Singleton3(){
        Tools.println("类实例化");
    }

    public static synchronized Singleton3 getInstance(){
        if(singleton2 == null)
            singleton2 = new Singleton3();
        return singleton2;
    }

    public static void CreateString(){
        Tools.print("Create String in Singleton3");
    }
}

懒汉模式通过getInstance()方法创建实例,这样只有当行使到实例的时刻才会初始化实例对象,实现了延加载,但是这种模式会现出线程同步问题:一个线程调用了getInstace()方法,判断为空后展开实例的创导,此时还要闹了一个线程调用了getInstance()方法,但此刻首先单线程还未曾做到实例化的操作,故此线程也会见实例化一个对象。所以我们得吗getInstance()方法加上一头关键字synchronized

那问题来了,我们以延缓加载就是为提升系统特性,而引入了一块关键字则会大大影响多线程情况下的特性,所以这个道啊发出就可怜充分的短处。

 

脚就是引入了再度检测方法

 

重新检测方法:通过还检测的点子就延迟加载

代码如下:

class Singleton1 {

    private Singleton1() {
    }

    public static Singleton1 instance = null;

    public static Singleton1 getInstance() {
        if (instance == null) {
            synchronized (Singleton1.class) {
                if (instance == null) {
                    instance = new Singleton1();
                }
            }
        }
        return instance;
    }
}

得看到,首先判断实例对象是否也空,如果判断通过重复拓展同步操作。

这种办法是化解了懒汉模式之效率问题,但还要为出局部题材,第一糟加载时反应不快,由于java内存模型一些缘故偶尔失败。失败原因可以详细http://blog.csdn.net/chenchaofuck1/article/details/51702129

 

通下去引入一种植都比较圆满而利用比较多的均等栽实现方式:静态内部类实现

代码如下:

public class Singleton4 {

private Singleton4(){}

static class SingletonHolder {

private static Singleton4 singleton = new Singleton4();

}

public static Singleton4 getInstance(){

return SingletonHolder.singleton;

}

}

以此措施使了:静态内部类并无见面在表面类类加载的时节呢进展类似加载,而是以其自己第一破吃采用时开展类似加载,并且jvm的好像加载过程是针对线程非常要好之,所以我们无需要担心联合问题。

public class Singleton4 {

    private Singleton4(){}

    static class SingletonHolder {
        private static Singleton4 singleton = new Singleton4();
    }

    public static Singleton4 getInstance(){
        return SingletonHolder.singleton;
    }
}

 

上述措施还是大半实现了单例模式,但是仍然有零星只问题亟需留意:

1:如果单例由不同之类装载器注入,那边有或有发生差不多个单例类的实例。假如不是远端存取,假如有servlet容器对每个servlet使用不同之类装载器,他们就是会生只字的单例类的实例。

2:如果单例类实现了java.io.Serializable接口,那么此类的实例就可被序列化和回复,如果序列化一个目标,然后还原多单是目标,就见面现出多只单例类的实例。

至于此问题可参见者文章:http://blog.csdn.net/fg2006/article/details/6409423

 

率先只问题之缓解措施:

private static Class getClass(String classname)      
                                         throws ClassNotFoundException {     
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     

      if(classLoader == null)     
         classLoader = Singleton.class.getClassLoader();     

      return (classLoader.loadClass(classname));     
   }     
}

亚个问题之化解方法:

public class Singleton implements java.io.Serializable {     
   public static Singleton INSTANCE = new Singleton();     

   protected Singleton() {     

   }     
   private Object readResolve() {     
            return INSTANCE;     
      }    
}  

 

当下片种植艺术是本人从其他的博客及看来的,现在尚于询问中。。。

 

 

使场景:

  1. Windows的Task
    Manager(任务管理器)就是很独立的单例模式(这个坏熟悉吧),想想看,是未是也,你会开拓两个windows
    task manager吗? 不信教你协调试看哦~ 

  2. windows的Recycle
    Bin(回收站)也是首屈一指的单例应用。在全体系运行过程遭到,回收站一直维护着才部分一个实例。

  3. 网站的计数器,一般为是使用单例模式实现,否则难以同。

4.
应用程序的日志应用,一般都何用单例模式实现,这相似是由共享的日志文件直接处于打开状态,因为只能发出一个实例去操作,否则内容不好搭。

5.
Web应用之布局对象的读取,一般也下单例模式,这个是由配备文件是共享的资源。

6. 数据库连接池的规划一般为是使单例模式,因为数据库连接是千篇一律种植数据库资源。数据库软件系统受应用数据库连接池,主要是节打开或者关闭数据库连接所引起的频率损耗,这种频率及之耗费还是很昂贵的,因为何用单例模式来保安,就好大大降低这种损耗。

7.
差不多线程的线程池的规划一般也是动单例模式,这是由线程池要便于对池中的线程进行支配。

8.
操作系统的文件系统,也是老之单例模式实现的实际事例,一个操作系统只能有一个文件系统。

9. HttpApplication
也是单位例的独立以。熟悉ASP.Net(IIS)的满贯请求生命周期的人应掌握HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.

 

总以上,不难看出:

  单例模式采用之气象一般发现以以下条件下:

  (1)资源共享的情下,避免由于资源操作时造成的特性还是吃等。如上述被之日志文件,应用配置。

  (2)控制资源的动静下,方便资源之间的并行通信。如丝程池等。

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注