设计模式之单例模式

创建型模式将对象的创建和使用分离,在使用对象时无需关注对象的创建细节,从而降低系统的耦合度,让设计方案更易于修改和扩展。

模式名称 定义 学习难度 使用频率
单例模式 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例 一颗星 四颗星
简单工厂模式 定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类 两颗星 三颗星
工厂方法模式 定义一个用于创建对象的接口,让子类决定将哪一个类实例化 两颗星 五颗星
抽象工厂模式 提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类 四颗星 五颗星
原型模式 使用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象 三颗星 三颗星
建造者模式 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 四颗星 两颗星

 

 

 

 

 

 

 

一、单例模式定义:确保某一个类只有一个实例,可以自行实例化并向整个系统提供这个实例,这个类成为单例类,它提供全局访问的方法。

二、实例:

 1 class TaskManager
 2 {
 3     private static TaskManager tm = null;
 4     
 5     private TaskManager(){...} //构造方法
 6     public void displayProcess(){...} //自定义方法
 7     public void displayServices(){...} //自定义方法
 8     
 9     public static TaskManager GetInstance()
10     {
11         if(tm == null)
12             tm = new TaskManager();
13         return tm;
14     }
15 }

1. 为了确保TaskManager实例的唯一性,需要禁止类的外部直接使用new来创建对象,因此需要将TaskManager的构造函数的可见性改为private。

2. 外部想要得到TaskManager类的实例,需要通过方法GetInstance(),获得TaskManager内部创建的实例tm。

3. 由于类外无法创建TaskManager对象,故需将GetInstance()定义为静态方法,方便类外可以直接通过类名访问。

4. 第一次调用GetInstance()方法时将创建唯一实例,再次调用时将返回第一次创建的实例。

三、饿汉式单例和懒汉式单例

1. 饿汉式单例:当类被加载时,静态变量tm会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。

 1 class TaskManager
 2 {
 3     private static TaskManager tm = new TaskManager();
 4     
 5     private TaskManager(){...}
 6     public void displayProcess(){...}
 7     public void displayServices(){...}
 8     
 9     public static TaskManager GetInstance()
10     {
11         return tm;
12     }
13 }

2. 懒汉式单例:即在第一次调用GetInstance方法时实例化,在类加载时并不自行实例化,这种技术又称为延迟加载(Lazy Load)技术,即需要的时候再加载实例。

为了避免多个线程同事调用GetInstance方法,可以使用锁。

class LazySingleton
{
        private static LazySingleton instance = null;
       
        private object SyncSelectObject = new object();

        private LazySingleton(){}  
       
        public static  LazySingleton GetInstance()
        {
                lock(SyncSelectObject )
                {
                    if (instance == null)
                    { 
                            instance = new LazySingleton ();
                    }
                    return instance;
               }                
        }
}    

 

上述代码虽然解决了线程安全问题,但是每次调用GetInstance时都需要进行线程锁定判断,将会导致系统性能大大降低。

饿汉式单例在类被加载时就将自己实例化,它的优点在于无序考虑多线程访问问题,可以确保实例的唯一性。

  • 从调用速度和反应时间角度来讲,由于单例对象一开始就得以创建,隐藏要优于懒汉式单例。
  • 但在类加载时该对象就需要创建,因此从资源利用效率角度来讲,饿汉式单例不及懒汉式单例,且系统加载时间可能会比较长。

懒汉式单例实现了延迟加载,但必须处理好多个线程同时访问的问题,这意味着出现多线程同时首次引用此类的概率变的较大,需要通过加锁进行控制,这将导致系统性能受到一定影响。

四、IoDH(Initialization on Demand Holder)技术

IoDH能够将两种单例的缺点都克服,而将两者的有点合二为一。

实现IoDH时,需在单例类中增加一个静态(static)内部类,在该内部类中创建单例对象,再将该单例对象通过getInstance方法返回给外部使用。

class Singleton
{
    private Singleton(){}  
    private static class HolderClass
    {
        private final static Singleton instance = new Singleton();
    }

    private static Singleton GetInstance()
    {
        return HolderClass.instance;
    }
   
    public static void main(String args[])
    {
        Singleton s1,s2;
        s1 = Singleton.GetInstance();
        s2 = Singleton.GetInstance();
        System.out.println(s1 == s2);//true
    }
}   

 由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton。第一次调用GetInstance时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量。由于GetInstance()方法没有被任何线程锁定,因此其性能不会造成任何影响。

posted @ 2024-08-24 12:24  LXLR  阅读(1)  评论(0编辑  收藏  举报