并且自行实例化并向一切体系提供那一个实例,图3-1 Windows职责管理器

【学习难度:★☆☆☆☆,使用频率:★★★★☆】

【学习难度:★☆☆☆☆,使用功用:★★★★☆】
一向出处:http://woquanke.com/books/gof/
梳理和学习:https://github.com/BruceOuyang/boy-design-pattern
简书日期: 2018/03/06
简书首页:https://www.jianshu.com/p/0fb891a7c5ed

定义

  单例情势(Singleton
Pattern):确保某多个类只有二个实例,而且自行实例化并向全方位连串提供那一个实例,那么些类称为单例类,它提供全局访问的法门。单例情势是一种目的创制型模式。

3.1 单例情势的念头

      对于二个软件系统的一些类而言,大家不要创制两个实例。举个大家都熟练的事例——Windows职分管理器,如图3-1所示,大家得以做3个如此的尝试,在Windows的“职责栏”的右键弹出菜谱上反复点击“运营任务管理器”,看可以如故不可以打开四个任务管理器窗口?假设您的桌面出现多个职分管理器,作者请你吃饭,图片 1(注:电脑中毒或专断修改Windows内核者除外)。平时境况下,无论大家运转义务管理有些次,Windows系统始终只好弹出2个职分管理器窗口,约等于说在贰个Windows系统中,任务管理器存在唯一性。为啥要那样设计吧?大家得以从以下八个方面来分析:其一,若是能弹出多少个窗口,且那几个窗口的情节完全一致,全体是双重对象,这势必会浪费系统财富,职分管理器必要取得系统运维时的无数新闻,这个音信的得到必要开销一定的系统能源,包含CPU财富及内存财富等,浪费是丢人的,而且一贯没有须求呈现多少个内容完全相同的窗口;其二,倘若弹出的三个窗口内容不同,难题就特别严重了,那代表在某一眨眼间间系统财富使用情形和进度、服务等新闻留存四个状态,例如职分管理器窗口A突显“CPU使用率”为十分一,窗口B显示“CPU使用率”为15%,到底哪个才是实在的啊?那纯属“调戏”用户,图片 2,给用户带来误解,更不可取。一言以蔽之,确保Windows任务管理器在系统中有且仅有一个不胜紧要。

图片 3

         图3-1 Windows义务管理器

      回到实际支付中,我们也时不时遇到类似的情状,为了省去系统能源,有时要求确保系统中某些类唯有唯一1个实例,当那几个唯一实例成立成功之后,我们不只怕再创立3个同类其他此外对象,全部的操作都只可以根据这一个唯一实例。为了保障目的的唯一性,大家可以透过单例方式来兑现,这就是单例格局的遐思所在。

 

管教目的的唯一性——单例情势 (一)

3.1 单例形式的胸臆

对于三个软件系统的一点类而言,大家毫不成立多少个实例。举个我们都熟悉的事例——Windows义务管理器,如图3-1所示,大家能够做多个如此的品味,在Windows的“任务栏”的右键弹出菜谱上数十三回点击“运转义务管理器”,看行依然不行打开五个任务管理器窗口?即使你的桌面出现三个职分管理器,作者请你吃饭,微笑(注:电脑中毒或地下修改Windows内核者除外)。

常见景况下,无论大家运营职务管理有个别次,Windows系统始终只可以弹出2个职务管理器窗口,也等于说在二个Windows系统中,职务管理器存在唯一性。

为何要那样设计呢?大家得以从以下四个地方来分析:

  • 以此,即便能弹出三个窗口,且那些窗口的始末完全一致,全体是再度对象,这势必会浪费系统财富,职责管理器要求得到系统运转时的成百上千音信,那一个消息的拿走必要消耗一定的系统财富,包罗CPU财富及内存财富等,浪费是羞耻的,而且根本没有要求显示七个内容完全相同的窗口;

  • 其二,如若弹出的七个窗口内容不均等,难点就进一步严重了,那象征在某一一眨眼系统能源使用景况和进度、服务等新闻存在八个情景,例如义务管理器窗口A突显“CPU使用率”为一成,窗口B彰显“CPU使用率”为15%,到底哪个才是全神关注的呢?那纯属“调戏”用户,偷笑,给用户带来误解,更不可取。

有鉴于此,确保Windows职责管理器在系统中有且仅有一个丰盛紧要。

图片 4

图3-1 Windows任务管理器

回来实际付出中,我们也平常蒙受类似的景况,为了节约系统财富,有时须求有限支撑系统中有个别类只有唯一壹个实例,当那么些唯一实例创设成功之后,我们无能为力又创制3个同体系的其余对象,全体的操作都只可以根据这几个唯一实例。为了保障目的的唯一性,大家得以经过单例方式来贯彻,那就是单例方式的想法所在。

3.2 单例情势概述

下边我们来模拟完结Windows职分管理器,假若职务管理器的类名为TaskManager,在TaskManager类中带有了汪洋的积极分子方法,例如构造函数TaskManager(),突显进程的法门displayProcesses(),显示服务的法门displayServices()等,该类的表示代码如下:

class TaskManager  
{  
     public TaskManager() {……} //初始化窗口  
     public void displayProcesses()  {……} //显示进程  
     public void  displayServices() {……} //显示服务  
     ……  
}

为了兑现Windows职务管理器的唯一性,大家经过如下三步来对此类进行重构:

(1)
由于每便使用new关键字来实例化TaskManager类时都将生出三个新目标,为了保证TaskManager实例的唯一性,大家要求禁止类的外表直接动用new来创设对象,由此须求将TaskManager的构造函数的可知性改为private,如下代码所示:

private TaskManager() {……}

(2)
将构造函数改为private修饰后该怎么创建对象呢?不要焦躁,固然类的表面不只怕再使用new来创制对象,可是在TaskManager的里边还可以创制的,可知性只对类外有效。因而,我们得以在TaskManager中创设并保存那么些唯一实例。为了让外界可以访问那一个唯一实例,须要在TaskManager中定义3个静态的TaskManager类型的个人成员变量,如下代码所示:

private static TaskManager tm = null;

(3)
为了保障成员变量的封装性,大家将TaskManager类型的tm对象的可知性设置为private,但外界该怎么样运用该成员变量并什么时候实例化该成员变量呢?答案是充实三个国有的静态方法,如下代码所示:

public static TaskManager getInstance()  
{  
    if (tm == null)  
    {  
        tm = new TaskManager();  
    }  
    return tm;  
}

在getInstance()方法中首先判断tm对象是还是不是留存,若是不存在(即tm ==
null),则选拔new关键字创造贰个新的TaskManager类型的tm对象,再回来新创立的tm对象;否则直接回到已有个别tm对象。

急需留意的是getInstance()方法的修饰符,首先它应该是3个public方法,以便供外界其他对象使用,其次它应用了static关键字,即它是一个静态方法,在类外可以平素通过类名来访问,而无须创造TaskManager对象,事实上在类外也无法成立TaskManager对象,因为构造函数是私有的。

思考

为何要将成员变量tm定义为静态变量?

经过上述多少个步骤,大家成功了八个最不难易行的单例类的统筹,其完整代码如下:

class TaskManager  
{  
     private static TaskManager tm = null;  
     private TaskManager() {……} //初始化窗口  
     public void  displayProcesses() {……} //显示进程  
     public void  displayServices() {……} //显示服务  
     public static TaskManager getInstance()  
     {  
        if (tm == null)  
        {  
            tm = new TaskManager();  
        }  
        return tm;  
    }  
   ……  
}

在类外我们无能为力直接开立新的TaskManager对象,但足以透过代码TaskManager.getInstance()来拜会实例对象,第四回调用getInstance()方法时将创建唯一实例,再一次调用时将重临第二回创造的实例,从而确保实例对象的唯一性。

上述代码也是单例形式的一种最惊叹不已完结情势,有了以上基础,明白单例方式的概念和社团就相当不难了。单例形式定义如下:
单例形式(Singleton
Pattern):确保某多个类唯有1个实例,而且自行实例化并向任何系统提供这些实例,这些类称为单例类,它提供全局访问的不二法门。单例情势是一种对象创制型情势。

单例格局有七个要点:一是有个别类只可以有一个实例;二是它必须自行创制这几个实例;三是它必须自行向整个序列提供那几个实例。

单例格局是构造最简便易行的设计方式一,在它的为主结构中只含有五个被叫做单例类的特殊类。单例方式结构如图3-2所示:

图片 5

单例情势结构图3-2

单例形式结构图中只蕴涵三个单例剧中人物:

  • Singleton(单例):在单例类的内部贯彻只生成多个实例,同时它提供八个静态的getInstance()工厂方法,让客户能够访问它的唯一实例;为了防备在外表对实际例化,将其构造函数设计为个体;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。

结构图

图片 6

要素

  • 有个别类只好有二个实例
  • 不可以不自行成立那几个实例
  • 非得自行向全部种类提供这几个实例

角色

  • Singleton(单例):在单例类的其中贯彻只生成一个实例,同时它提供二个静态的getInstance()工厂方法,让客户能够访问它的唯一实例;为了防止在外部对实际例化,将其构造函数设计为私家;在单例类内部定义了二个Singleton类型的静态对象,作为外部共享的绝无仅有实例。

3.2 单例格局概述

      上边大家来效仿落成Windows职务管理器,倘使职分管理器的类名为TaskManager,在TaskManager类中带有了多量的成员方法,例如构造函数TaskManager(),显示进度的方法displayProcesses(),展现服务的法子displayServices()等,该类的示意代码如下:

[java] view
plain
 copy

 

 图片 7图片 8

  1. class TaskManager  
  2. {  
  3.      public TaskManager() {……} //初阶化窗口  
  4.      public void displayProcesses()  {……} //展现进度  
  5.      public void  displayServices() {……} //展现服务  
  6.      ……  
  7. }  

       为了落到实处Windows任务管理器的唯一性,大家由此如下三步来对此类进行重构:

     
(1)  由于每便使用new关键字来实例化TaskManager类时都将爆发一个新目的,为了保障TaskManager实例的唯一性,大家需求禁止类的外部直接运用new来创制对象,因而须要将TaskManager的构造函数的可知性改为private,如下代码所示:

[java] view
plain
 copy

 

 图片 9图片 10

  1. private TaskManager() {……}  

      
(2)  将构造函数改为private修饰后该怎么创设对象呢?不要着急,即便类的外表无法再利用new来成立对象,可是在TaskManager的其中还可以成立的,可知性只对类外有效。由此,大家可以在TaskManager中开创并保留那么些唯一实例。为了让外界可以访问那么些唯一实例,须求在TaskManager中定义多个静态的TaskManager类型的民用成员变量,如下代码所示:

[java] view
plain
 copy

 

 图片 11图片 12

  1. private static TaskManager tm = null;  

      (3)  为了保证成员变量的封装性,大家将TaskManager类型的tm对象的可知性设置为private,但外围该如何拔取该成员变量并曾几何时实例化该成员变量呢?答案是扩展贰个国有的静态方法,如下代码所示:

[java] view
plain
 copy

 

 图片 13图片 14

  1. public static TaskManager getInstance()  
  2. {  
  3.     if (tm == null)  
  4.     {  
  5.         tm = new TaskManager();  
  6.     }  
  7.     return tm;  
  8. }  

       在getInstance()方法中第3判断tm对象是或不是存在,假若不设有(即tm ==
null),则运用new关键字创设一个新的TaskManager类型的tm对象,再回到新创立的tm对象;否则直接重回已部分tm对象。

     
要求专注的是getInstance()方法的修饰符,首先它应有是2个public方法,以便供外界其他对象使用,其次它应用了static关键字,即它是八个静态方法,在类外可以一直通过类名来访问,而无须创制TaskManager对象,事实上在类外也无力回天创立TaskManager对象,因为构造函数是私房的。 

 

思考

为什么要将成员变量tm定义为静态变量?

 

       通过以上多少个步骤,大家成功了三个最简单易行的单例类的设计,其完整代码如下:

class TaskManager  
{  
     private static TaskManager tm = null;  
     private TaskManager() {……} //初始化窗口  
     public void  displayProcesses() {……} //显示进程  
     public void  displayServices() {……} //显示服务  
     public static TaskManager getInstance()  
     {  
        if (tm == null)  
        {  
            tm = new TaskManager();  
        }  
        return tm;  
    }  
   ……  
}  

 在类外大家无能为力直接开立新的TaskManager对象,但可以透过代码TaskManager.getInstance()来做客实例对象,第两次调用getInstance()方法时将创立唯一实例,再一次调用时将回来第两遍创造的实例,从而保险实例对象的唯一性。

      上述代码也是单例形式的一种最叹为观止落成方式,有了上述基础,领悟单例情势的定义和布局就非凡不难了。单例格局定义如下: 

单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。

     
单例格局有多少个中央:一是有个别类只好有2个实例;二是它必须自行创制那么些实例;三是它必须自行向全方位连串提供那一个实例。

      
单例格局是结构最简易的设计格局一,在它的为主结构中只含有三个被叫做单例类的独特类。单例格局协会如图3-2所示:

图片 15

      单例方式社团图中只含有3个单例剧中人物:

     
● Singleton(单例):在单例类的中间贯彻只生成三个实例,同时它提供贰个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防备在外部对实际例化,将其构造函数设计为个人;在单例类内部定义了3个Singleton类型的静态对象,作为外部共享的绝无仅有实例。

保证目的的唯一性——单例方式 (二)

3.3 负载均衡器的安排与落成

Sunny软件公司承接了1个服务器负荷均衡(Load
Balance)软件的开发工作,该软件运营在一台载荷均衡服务器上,可以将现出访问和数码流量分发到服务器集群中的多台设备上展开并发处理,进步系统的完整处理能力,减少响应时间。由于集群中的服务器要求动态删减,且客户端请求须要联合分发,由此须要保障负载均衡器的唯一性,只好有一个载重均衡器来顶住服务器的治本和呼吁的散发,否则将会拉动服务器状态的不均等以及呼吁分配冲突等题材。如何保障负载均衡器的唯一性是该软件成功的紧要性。

Sunny集团开发人士通过分析和权衡,决定接纳单例格局来陈设该负载均衡器,结构图如图3-3所示:

图片 16

服务器负荷均衡器结构图

在图3-3中,将负载均衡器LoadBalancer设计为单例类,其中涵盖1个囤积服务器音信的集合serverList,每一遍在serverList中随心所欲接纳一台服务器来响应客户端的央浼,达成代码如下所示:

import java.util.*;  

//负载均衡器LoadBalancer:单例类,真实环境下该类将非常复杂,包括大量初始化的工作和业务方法,考虑到代码的可读性和易理解性,只列出部分与模式相关的核心代码  
class LoadBalancer {  
    //私有静态成员变量,存储唯一实例  
    private static LoadBalancer instance = null;  
    //服务器集合  
    private List serverList = null;  

    //私有构造函数  
    private LoadBalancer() {  
        serverList = new ArrayList();  
    }  

    //公有静态成员方法,返回唯一实例  
    public static LoadBalancer getLoadBalancer() {  
        if (instance == null) {  
            instance = new LoadBalancer();  
        }  
        return instance;  
    }  

    //增加服务器  
    public void addServer(String server) {  
        serverList.add(server);  
    }  

    //删除服务器  
    public void removeServer(String server) {  
        serverList.remove(server);  
    }  

    //使用Random类随机获取服务器  
    public String getServer() {  
        Random random = new Random();  
        int i = random.nextInt(serverList.size());  
        return (String)serverList.get(i);  
    }  
}

编排如下客户端测试代码:

class Client {  
    public static void main(String args[]) {  
        //创建四个LoadBalancer对象  
        LoadBalancer balancer1,balancer2,balancer3,balancer4;  
        balancer1 = LoadBalancer.getLoadBalancer();  
        balancer2 = LoadBalancer.getLoadBalancer();  
        balancer3 = LoadBalancer.getLoadBalancer();  
        balancer4 = LoadBalancer.getLoadBalancer();  

        //判断服务器负载均衡器是否相同  
        if (balancer1 == balancer2 && balancer2 == balancer3 && balancer3 == balancer4) {  
            System.out.println("服务器负载均衡器具有唯一性!");  
        }  

        //增加服务器  
        balancer1.addServer("Server 1");  
        balancer1.addServer("Server 2");  
        balancer1.addServer("Server 3");  
        balancer1.addServer("Server 4");  

        //模拟客户端请求的分发  
        for (int i = 0; i < 10; i++) {  
            String server = balancer1.getServer();  
            System.out.println("分发请求至服务器: " + server);  
      }  
    }  
}

编译并运维程序,输出结果如下:

服务器负载均衡器具有唯一性!
分发请求至服务器:  Server 1
分发请求至服务器:  Server 3
分发请求至服务器:  Server 4
分发请求至服务器:  Server 2
分发请求至服务器:  Server 3
分发请求至服务器:  Server 2
分发请求至服务器:  Server 3
分发请求至服务器:  Server 4
分发请求至服务器:  Server 4
分发请求至服务器:  Server 1

虽说创制了多少个LoadBalancer对象,不过它们其实是同一个对象,因而,通过拔取单例情势可以保障LoadBalancer对象的唯一性。

示例

  Sunny软件商店承接了二个服务器负荷均衡(Load
Balance)软件的用度工作,该软件运转在一台载荷均衡服务器上,可以将出现访问和数码流量分发到服务器集群中的多台设备上展开并发处理,提升系统的一体化处理能力,收缩响应时间。由于集群中的服务器必要动态删减,且客户端请求须要统一分发,由此须要确保负载均衡器的唯一性,只可以有二个载荷均衡器来顶住服务器的田间管理和央浼的分发,否则将会牵动服务器状态的不平等以及呼吁分配争持等难题。怎么样保管负载均衡器的唯一性是该软件成功的主要。

import java.util.*;  

//负载均衡器LoadBalancer:单例类,真实环境下该类将非常复杂,包括大量初始化的工作和业务方法,考虑到代码的可读性和易理解性,只列出部分与模式相关的核心代码  
class LoadBalancer {  
    //私有静态成员变量,存储唯一实例  
    private static LoadBalancer instance = null;  
    //服务器集合  
    private List serverList = null;  

    //私有构造函数  
    private LoadBalancer() {  
        serverList = new ArrayList();  
    }  

    //公有静态成员方法,返回唯一实例  
    public static LoadBalancer getLoadBalancer() {  
        if (instance == null) {  
            instance = new LoadBalancer();  
        }  
        return instance;  
    }  

    //增加服务器  
    public void addServer(String server) {  
        serverList.add(server);  
    }  

    //删除服务器  
    public void removeServer(String server) {  
        serverList.remove(server);  
    }  

    //使用Random类随机获取服务器  
    public String getServer() {  
        Random random = new Random();  
        int i = random.nextInt(serverList.size());  
        return (String)serverList.get(i);  
    }  
}  

class Client {  
    public static void main(String args[]) {  
        //创建四个LoadBalancer对象  
        LoadBalancer balancer1,balancer2,balancer3,balancer4;  
        balancer1 = LoadBalancer.getLoadBalancer();  
        balancer2 = LoadBalancer.getLoadBalancer();  
        balancer3 = LoadBalancer.getLoadBalancer();  
        balancer4 = LoadBalancer.getLoadBalancer();  

        //判断服务器负载均衡器是否相同  
        if (balancer1 == balancer2 && balancer2 == balancer3 && balancer3 == balancer4) {  
            System.out.println("服务器负载均衡器具有唯一性!");  
        }  

        //增加服务器  
        balancer1.addServer("Server 1");  
        balancer1.addServer("Server 2");  
        balancer1.addServer("Server 3");  
        balancer1.addServer("Server 4");  

        //模拟客户端请求的分发  
        for (int i = 0; i < 10; i++) {  
            String server = balancer1.getServer();  
            System.out.println("分发请求至服务器: " + server);  
      }  
    }  
}  

留存难题
  当第三遍调用getLoadBalancer()方法创造并运营负载均衡器时,instance对象为null值,因而系统将履行代码instance=
new
LoadBalancer(),在此进程中,由于要对LoadBalancer进行大气早先化工作,须求一段时间来成立LoadBalancer对象。而在那时,如果再两遍调用getLoadBalancer()方法(经常发生在三八线程环境中),由于instance尚未创制成功,仍为null值,判断标准(instance==
null)为真值,因而代码instance= new
LoadBalancer()将另行实施,导致最后创设了两个instance对象,那违反了单例形式的初衷,也导致系统运转爆发错误。
消除方案

  • 饿汉式单例类
![](https://upload-images.jianshu.io/upload_images/2359939-da196733bb995b8b.png)

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

    public static EagerSingleton getInstance() {  
        return instance;   
    }     
}  
  • 懒汉式单例类与线程锁定
![](https://upload-images.jianshu.io/upload_images/2359939-bad4f6add973c2eb.png)

class LazySingleton {   
    private static LazySingleton instance = null;   

    private LazySingleton() { }   

    synchronized public static LazySingleton getInstance() {   
        if (instance == null) {  
            instance = new LazySingleton();   
        }  
        return instance;   
    }  
}  

留存难点:老是调用getInstance()时都须求举行线程锁定判断,在三十二线程高并发访问环境中,将会导致系统质量大大下落

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

3.3 负载均衡器的陈设与落实

       Sunny软件公司承接了一个服务器负载均衡(Load Balance)软件的开发工作,该软件运行在一台负载均衡服务器上,可以将并发访问和数据流量分发到服务器集群中的多台设备上进行并发处理,提高系统的整体处理能力,缩短响应时间。由于集群中的服务器需要动态删减,且客户端请求需要统一分发,因此需要确保负载均衡器的唯一性,只能有一个负载均衡器来负责服务器的管理和请求的分发,否则将会带来服务器状态的不一致以及请求分配冲突等问题。如何确保负载均衡器的唯一性是该软件成功的关键。

      Sunny公司开发人士通过分析和权衡,决定动用单例方式来统筹该负载均衡器,结构图如图3-3所示:

图片 17

        在图3-3中,将负载均衡器LoadBalancer设计为单例类,其中包涵1个储存服务器音讯的集合serverList,每一遍在serverList中随意挑选一台服务器来响应客户端的央浼,完毕代码如下所示:

import java.util.*;  

//负载均衡器LoadBalancer:单例类,真实环境下该类将非常复杂,包括大量初始化的工作和业务方法,考虑到代码的可读性和易理解性,只列出部分与模式相关的核心代码  
class LoadBalancer {  
    //私有静态成员变量,存储唯一实例  
    private static LoadBalancer instance = null;  
    //服务器集合  
    private List serverList = null;  

    //私有构造函数  
    private LoadBalancer() {  
        serverList = new ArrayList();  
    }  

    //公有静态成员方法,返回唯一实例  
    public static LoadBalancer getLoadBalancer() {  
        if (instance == null) {  
            instance = new LoadBalancer();  
        }  
        return instance;  
    }  

    //增加服务器  
    public void addServer(String server) {  
        serverList.add(server);  
    }  

    //删除服务器  
    public void removeServer(String server) {  
        serverList.remove(server);  
    }  

    //使用Random类随机获取服务器  
    public String getServer() {  
        Random random = new Random();  
        int i = random.nextInt(serverList.size());  
        return (String)serverList.get(i);  
    }  
}  

编辑如下客户端测试代码:

class Client {  
    public static void main(String args[]) {  
        //创建四个LoadBalancer对象  
        LoadBalancer balancer1,balancer2,balancer3,balancer4;  
        balancer1 = LoadBalancer.getLoadBalancer();  
        balancer2 = LoadBalancer.getLoadBalancer();  
        balancer3 = LoadBalancer.getLoadBalancer();  
        balancer4 = LoadBalancer.getLoadBalancer();  

        //判断服务器负载均衡器是否相同  
        if (balancer1 == balancer2 && balancer2 == balancer3 && balancer3 == balancer4) {  
            System.out.println("服务器负载均衡器具有唯一性!");  
        }  

        //增加服务器  
        balancer1.addServer("Server 1");  
        balancer1.addServer("Server 2");  
        balancer1.addServer("Server 3");  
        balancer1.addServer("Server 4");  

        //模拟客户端请求的分发  
        for (int i = 0; i < 10; i++) {  
            String server = balancer1.getServer();  
            System.out.println("分发请求至服务器: " + server);  
      }  
    }  
}  

   编译并运维程序,输出结果如下:

服务器负载均衡器具有唯一性!

分发请求至服务器:  Server 1

分发请求至服务器:  Server 3

分发请求至服务器:  Server 4

分发请求至服务器:  Server 2

分发请求至服务器:  Server 3

分发请求至服务器:  Server 2

分发请求至服务器:  Server 3

分发请求至服务器:  Server 4

分发请求至服务器:  Server 4

分发请求至服务器:  Server 1

        就算创设了七个LoadBalancer对象,不过它们其实是同3个目的,因而,通过应用单例形式可以保障LoadBalancer对象的唯一性。

管教目的的唯一性——单例格局 (三)

3.4 饿汉式单例与懒汉式单例的研讨

Sunny集团开发人士使用单例方式完毕了负荷均衡器的安顿,然则在实际应用中冒出了3个十二分沉痛的题材,当负载均衡器在开行进程中用户再一次运转该负载均衡器时,系统无任何尤其,但当客户端提交请求时出现请求分发失利,通过仔细分析发现原先系统中要么存在多个负载均衡器对象,导致分发时目的服务器不等同,从而爆发争辨。为啥会如此呢?Sunny公司开发人士百思不得其解。

前些天大家对负荷均衡器的落到实处代码进行双重分析,当第⑥回调用getLoadBalancer()方法创设并运维负载均衡器时,instance对象为null值,由此系统将履行代码instance=
new
LoadBalancer(),在此进程中,由于要对LoadBalancer举办大气初叶化工作,必要一段时间来创设LoadBalancer对象。而在那时,假使再一遍调用getLoadBalancer()方法(常常发生在二十八线程环境中),由于instance尚未创设成功,仍为null值,判断标准(instance==
null)为真值,因此代码instance= new
LoadBalancer()将另行实施,导致最后创建了多少个instance对象,那违反了单例形式的初衷,也导致系统运营发生错误。

怎样化解该难点?我们足足有三种缓解方案,在正规介绍那三种缓解方案此前,先介绍一下单例类的二种不一样落成格局,饿汉式单例类和懒汉式单例类。

3.4.1.饿汉式单例类

饿汉式单例类是贯彻起来最不难易行的单例类,饿汉式单例类结构图如图3-4所示:

图片 18

饿汉式单例类结构图

从图3-4中得以见见,由于在概念静态变量的时候实例化单例类,由此在类加载的时候就已经创立了单例对象,代码如下所示:

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

    public static EagerSingleton getInstance() {  
        return instance;   
    }     
}

当类被加载时,静态变量instance会被初始化,此时类的私房构造函数会被调用,单例类的绝无仅有实例将被创制。倘使运用饿汉式单例来贯彻负载均衡器LoadBalancer类的规划,则不会并发创建多少个单例对象的动静,可确保单例对象的唯一性。

3.4.2.懒汉式单例类与线程锁定

除外饿汉式单例,还有一种经典的懒汉式单例,相当于目前的负荷均衡器LoadBalancer类的兑现方式。懒汉式单例类结构图如图3-5所示:

图片 19

懒汉式单例类结构图

从图3-5中可以看看,懒汉式单例在第两回调用getInstance()方法时实例化,在类加载时并不自动实例化,那种技术又称之为延迟加载(Lazy
Load)技术,即必要的时候再加载实例,为了避免多少个线程同时调用getInstance()方法,我们可以利用首要字synchronized,代码如下所示:

class LazySingleton {   
    private static LazySingleton instance = null;   

    private LazySingleton() { }   

    synchronized public static LazySingleton getInstance() {   
        if (instance == null) {  
            instance = new LazySingleton();   
        }  
        return instance;   
    }  
}

该懒汉式单例类在getInstance()方法前边伸张了严重性字synchronized进行线程锁,以拍卖多少个线程同时做客的题材。不过,上述代码即使缓解了线程安全题材,但是每一遍调用getInstance()时都亟待展开线程锁定判断,在三十二线程高并发访问环境中,将会导致系统品质大大降低。怎么样既消除线程安全题材又不影响系统性子呢?我们后续对懒汉式单例举行革新。事实上,大家不要对全体getInstance()方法开展锁定,只需对中间的代码“instance
= new
LazySingleton();”举行锁定即可。因而getInstance()方法可以拓展如下革新:

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

题材一般得以解决,事实并非如此。倘若利用上述代码来促成单例,依然会设有单例对象不唯一。原因如下:

存在难题:假定在某一眨眼间间线程A和线程B都在调用getInstance()方法,此时instance对象为null值,均能因而instance

null的判断。由于完成了synchronized加锁机制,线程A进入synchronized锁定的代码中实施实例成立代码,线程B处于排队等候意况,必须等待线程A执行落成后才方可进来synchronized锁定代码。但当A执行完成时,线程B并不知道实例已经创建,将继承创立新的实例,导致发生三个单例对象。
一发化解方案:重复检查锁定(Double-Check Locking)

class LazySingleton {   
    private volatile static LazySingleton instance = null;   

    private LazySingleton() { }   

    public static LazySingleton getInstance() {   
        //第一重判断  
        if (instance == null) {  
            //锁定代码块  
            synchronized (LazySingleton.class) {  
                //第二重判断  
                if (instance == null) {  
                    instance = new LazySingleton(); //创建单例实例  
                }  
            }  
        }  
        return instance;   
    }  
}  

3.4 饿汉式单例与懒汉式单例的议论

      萨妮集团开发人士使用单例情势完结了负荷均衡器的设计,可是在实际应用中出现了2个拾叁分沉痛的难点,当负载均衡器在开行进程中用户再一次运营该负载均衡器时,系统无别的尤其,但当客户端提交请求时出现请求分发失利,通过缜密分析发现原本系统中要么存在八个负载均衡器对象,导致分发时目标服务器不雷同,从而产生争辨。为何会这么吧?Sunny集团开发人员百思不得其解。

     
以往我们对负荷均衡器的兑现代码进行重新分析,当第两回调用getLoadBalancer()方法创立并运转负载均衡器时,instance对象为null值,由此系统将履行代码instance=
new
LoadBalancer(),在此进程中,由于要对LoadBalancer进行大气开头化工作,必要一段时间来创立LoadBalancer对象。而在那时,倘诺再两回调用getLoadBalancer()方法(经常爆发在多线程环境中),由于instance尚未创制成功,仍为null值,判断标准(instance==
null)为真值,因而代码instance= new
LoadBalancer()将另行实施,导致最后创建了多少个instance对象,这违背了单例形式的初衷,也导致系统运转发生错误。

     
怎样化解该难题?大家足足有二种缓解方案,在规范介绍那二种缓解方案之前,先介绍一下单例类的三种差别完结方式,饿汉式单例类和懒汉式单例类。

 

1.饿汉式单例类

     
饿汉式单例类是贯彻起来最简便的单例类,饿汉式单例类结构图如图3-4所示:

图片 20

       从图3-4中可以见到,由于在概念静态变量的时候实例化单例类,因而在类加载的时候就早已创办了单例对象,代码如下所示:

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

    public static EagerSingleton getInstance() {  
        return instance;   
    }     
}  

 

  当类被加载时,静态变量instance会被初阶化,此时类的个人构造函数会被调用,单例类的绝无仅有实例将被成立。即使拔取饿汉式单例来贯彻负载均衡器LoadBalancer类的设计,则不见面世创制两个单例对象的状态,可保障单例对象的唯一性。

 

2.懒汉式单例类与线程锁定

     
除了饿汉式单例,还有一种经典的懒汉式单例,约等于前方的载荷均衡器LoadBalancer类的贯彻方式。懒汉式单例类结构图如图3-5所示:

图片 21

     从图3-5中得以看看,懒汉式单例在第一回调用getInstance()方法时实例化,在类加载时并不自动实例化,那种技术又称作推迟加载(Lazy
Load)
技术,即须要的时候再加载实例,为了幸免多少个线程同时调用getInstance()方法,大家得以利用关键字synchronized,代码如下所示:

class LazySingleton {   
    private static LazySingleton instance = null;   

    private LazySingleton() { }   

    synchronized public static LazySingleton getInstance() {   
        if (instance == null) {  
            instance = new LazySingleton();   
        }  
        return instance;   
    }  
}  

 该懒汉式单例类在getInstance()方法前边扩张了严重性字synchronized举行线程锁,以处理多个线程同时做客的题材。可是,上述代码即便缓解了线程安全难题,可是每一趟调用getInstance()时都亟需举办线程锁定判断,在二十四线程高并发访问环境中,将会导致系统质量大大降低。怎么样既缓解线程安全难点又不影响系统天性呢?大家接二连三对懒汉式单例进行改革。事实上,我们绝不对全部getInstance()方法开展锁定,只需对内部的代码“instance
= new
LazySingleton();”进行锁定即可。由此getInstance()方法可以展开如下创新:

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

 难题一般得以消除,事实并非如此。固然应用上述代码来促成单例,如故会存在单例对象不唯一。原因如下:

     

如果在某一一晃线程A和线程B都在调用getInstance()方法,此时instance对象为null值,均能经过instance

null的判断。由于完毕了synchronized加锁机制,线程A进入synchronized锁定的代码中举行实例创立代码,线程B处于排队等候状态,必须等待线程A执行落成后才能够进去synchronized锁定代码。但当A执行落成时,线程B并不知道实例已经创办,将继续开立异的实例,导致爆发三个单例对象,违背单例方式的安顿思想,因而须求开展进一步改良,在synchronized中再展开三回(instance
== null)判断,那种方法叫做双重检查锁定(Double-Check Locking)。

使用重复检查锁定完毕的懒汉式单例类完整代码如下所示:

class LazySingleton {   
    private volatile static LazySingleton instance = null;   

    private LazySingleton() { }   

    public static LazySingleton getInstance() {   
        //第一重判断  
        if (instance == null) {  
            //锁定代码块  
            synchronized (LazySingleton.class) {  
                //第二重判断  
                if (instance == null) {  
                    instance = new LazySingleton(); //创建单例实例  
                }  
            }  
        }  
        return instance;   
    }  
}

亟需专注的是,倘使选用重复检查锁定来落到实处懒汉式单例类,须求在静态成员变量instance从前扩展修饰符volatile,被volatile修饰的成员变量可以保险多少个线程都可以正确处理,且该代码只可以在JDK
1.5及以上版本中才能科学履行。由于volatile关键字会屏蔽Java虚拟机所做的一对代码优化,或然会导致系统运维功效下落,由此即便使用重复检查锁定来贯彻单例情势也不是一种完美的贯彻方式。

扩展

IBM公司高等软件工程师Peter Haggar 二〇〇一年在IBM
developerWorks上公布了一篇名为《双重检查锁定及单例形式——周密精晓这一失效的编程习语》的小说,对JDK
1.5事先的双重检查锁定及单例格局开展了周全剖析和阐释,参考链接:http://www.ibm.com/developerworks/cn/java/j-dcl.html

3.4.3.饿汉式单例类与懒汉式单例类相比

饿汉式单例类在类被加载时就将协调实例化,它的长处在于不要考虑多线程访问难点,可以有限援救实例的唯一性;从调用速度和反应时间角度来讲,由于单例对象一起初就可以创制,由此要优化懒汉式单例。不过不管系统在运营时是不是需求利用该单例对象,由于在类加载时该目的就必要创立,因而从能源利用功效角度来讲,饿汉式单例不及懒汉式单例,而且在系统加载时由于须要创立饿汉式单例对象,加载时间或者会相比长。

懒汉式单例类在首先次采纳时成立,无须从来占有系统能源,完结了延期加载,不过必须处理好三个线程同时做客的标题,尤其是当单例类作为财富控制器,在实例化时肯定关系资源初叶化,而财富开头化很有恐怕损耗大批量日子,那代表现身十二线程同时首次引用此类的机率变得较大,要求通过重新检查锁定等体制进行控制,那将导致系统质量受到肯定影响。

饿汉式单例类与懒汉式单例类相比较

  • 饿汉式单例类在类被加载时就将协调实例化,它的助益在于不要考虑三二十四线程访问难题,能够保险实例的唯一性;从调用速度和反应时间角度来讲,由于单例对象一初步就可以创制,因而要优于懒汉式单例。不过不管系统在运作时是还是不是须要运用该单例对象,由于在类加载时该对象就需求成立,因而从财富利用效用角度来讲,饿汉式单例不及懒汉式单例,而且在系统加载时出于须求创设饿汉式单例对象,加载时间或然会比较长。
  • 懒汉式单例类在首先次使用时创设,无须平昔占据系统能源,完结了延期加载,可是非得处理好多少个线程同时做客的题材,尤其是当单例类作为财富控制器,在实例化时肯定关系财富开始化,而能源早先化很有大概损耗大批量年华,这表示出现三十二线程同时首次引用此类的机率变得较大,须要通过重新检查锁定等体制进行控制,那将导致系统品质受到肯定影响。

假使在某一一眨眼线程A和线程B都在调用getInstance()方法,此时instance对象为null值,均能透过instance

null的判断。由于完结了synchronized加锁机制,线程A进入synchronized锁定的代码中执行实例创立代码,线程B处于排队等候意况,必须等待线程A执行已毕后才得以进去synchronized锁定代码。但当A执行完成时,线程B并不知道实例已经创建,将继续开创新的实例,导致暴发八个单例对象,违背单例方式的统筹思想,由此需求开展更为改革,在synchronized中再拓展一回(instance
== null)判断,那种情势叫做再次检查锁定(Double-Check
Locking)
。使用重复检查锁定实现的懒汉式单例类完整代码如下所示:

class LazySingleton {   
    private volatile static LazySingleton instance = null;   

    private LazySingleton() { }   

    public static LazySingleton getInstance() {   
        //第一重判断  
        if (instance == null) {  
            //锁定代码块  
            synchronized (LazySingleton.class) {  
                //第二重判断  
                if (instance == null) {  
                    instance = new LazySingleton(); //创建单例实例  
                }  
            }  
        }  
        return instance;   
    }  
}  

 

要求专注的是,假如利用重复检查锁定来兑现懒汉式单例类,需要在静态成员变量instance从前扩展修饰符volatile,被volatile修饰的分子变量可以确保三个线程都可以正确处理,且该代码只可以在JDK
1.5及以上版本中才能科学执行。由于volatile关键字会屏蔽Java虚拟机所做的片段代码优化,或者会导致系统运转效用下跌,因而即使使用重复检查锁定来促成单例形式也不是一种完美的落实格局。 

 

扩展

IBM公司高级软件工程师Peter    Haggar 2004年在IBM developerWorks上发表了一篇名为《双重检查锁定及单例模式——全面理解这一失效的编程习语》的文章,对JDK    1.5之前的双重检查锁定及单例模式进行了全面分析和阐述,参考链接:http://www.ibm.com/developerworks/cn/java/j-dcl.html

 

3.饿汉式单例类与懒汉式单例类相比

     
饿汉式单例类在类被加载时就将协调实例化,它的助益在于不要考虑多线程访问难题,可以保证实例的唯一性;从调用速度和反应时间角度来讲,由于单例对象一先导就可以创造,因而要优于懒汉式单例。不过无论系统在运作时是否要求使用该单例对象,由于在类加载时该目的就必要创制,因而从能源利用成效角度来讲,饿汉式单例不及懒汉式单例,而且在系统加载时出于需求创制饿汉式单例对象,加载时间可能会比较长。

     
懒汉式单例类在率先次接纳时创制,无须一向占有系统财富,落成了延期加载,但是必须处理好三个线程同时做客的标题,尤其是当单例类作为能源控制器,在实例化时一定涉及能源初阶化,而资源开始化很有可能损耗大批量时刻,那意味着出现多线程同时第四回引用此类的机率变得较大,要求经过重复检查锁定等编制进行支配,那将导致系统质量受到一定影响。

 

保障目标的唯一性——单例格局 (四)

3.5 一种更好的单例达成方式

饿汉式单例类不可以兑现延迟加载,不管今后用不用始终占据内存;懒汉式单例类线程安全控制烦琐,而且品质受影响。可知,无论是饿汉式单例仍然懒汉式单例都设有这么那样的题材,有没有一种艺术,可以将三种单例的短处都克服,而将双边的长处合两为一吗?答案是:Yes!下边大家来读书那种更好的被称之为Initialization
德姆and Holder (IoDH)的技巧。

在IoDH中,大家在单例类中追加3个静态(static)内部类,在该内部类中创立单例对象,再将该单例对象通过getInstance()方法再次回到给外部使用,落成代码如下所示:

//Initialization on Demand Holder  
class Singleton {  
    private Singleton() {  
    }  

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

    public 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,即成立的单例对象s1和s2为同样对象。由于静态单例对象没有作为Singleton的成员变量直接实例化,由此类加载时不会实例化Singleton,第伍遍调用getInstance()时将加载内部类HolderClass,在该内部类中定义了1个static类型的变量instance,此时会率先起初化那几个成员变量,由Java虚拟机来确保其线程安全性,确保该成员变量只可以早先化两次。由于getInstance()方法没有其余线程锁定,因而其天性不会招致任何影响。

通过运用IoDH,大家既可以兑现延迟加载,又足以确保线程安全,不影响系统天性,不失为一种最好的Java语言单例情势已毕形式(其缺点是与编程语言本身的个性相关,很多面向对象语言不协理IoDH)。

练习

分级拔取饿汉式单例、带双重检查锁定机制的懒汉式单例以及IoDH技术完毕负载均衡器LoadBalancer。

锻炼会在我的github上做掉

从那之后,三种单例类的贯彻情势大家均已上学完成,它们各自是饿汉式单例、懒汉式单例以及IoDH。

尤为缓解方案:Initialization 德姆and Holder (IoDH)的技巧

关键:Java虚拟机来保证其线程安全性
//Initialization on Demand Holder  
class Singleton {  
    private Singleton() {  
    }  

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

    public 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);  
    }  
}  

3.5 一种更好的单例完成情势

       饿汉式单例类不可以落到实处延迟加载,不管将来用不用始终占据内存;懒汉式单例类线程安全控制烦琐,而且品质受影响。可知,无论是饿汉式单例照旧懒汉式单例都存在这样这样的标题,有没有一种模式,可以将三种单例的败笔都击败,而将二者的助益合而为一呢?答案是:Yes!上边大家来上学那种更好的被叫做Initialization
Demand Holder (IoDH)
的技术。

     
在IoDH中,大家在单例类中扩充二个静态(static)内部类,在该内部类中开创单例对象,再将该单例对象通过getInstance()方法重返给外部使用,落成代码如下所示:

//Initialization on Demand Holder  
class Singleton {  
    private Singleton() {  
    }  

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

    public 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,即创办的单例对象s1和s2为同一对象。由于静态单例对象没有当做Singleton的积极分子变量直接实例化,由此类加载时不会实例化Singleton,第四遍调用getInstance()时将加载内部类HolderClass,在该内部类中定义了二个static类型的变量instance,此时会首先初阶化这么些成员变量,由Java虚拟机来保障其线程安全性,确保该成员变量只可以初叶化一次。由于getInstance()方法没有其他线程锁定,因而其质量不会导致其他影响。

      因此运用IoDH,大家既可以落成延迟加载,又足以确保线程安全,不影响系统性子,不失为一种最好的Java语言单例情势达成情势(其缺点是与编程语言本人的表征相关,很多面向对象语言不扶助IoDH)。

 

练习

分别使用饿汉式单例、带双重检查锁定机制的懒汉式单例以及IoDH技术实现负载均衡器LoadBalancer。

 

      至此,三种单例类的落成形式我们均已上学完结,它们各自是饿汉式单例、懒汉式单例以及IoDH

保障目的的唯一性——单例格局 (五)

3.6 单例方式统计

单例形式作为一种目的一目通晓、结构简单、了解简单的设计情势,在软件开发中运用频率万分高,在重重施用软件和框架中都可以广泛应用。

  1. 最紧要优点

单例情势的最紧要优点如下:

(1)
单例方式提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严苛控制客户怎么以及什么时候造访它。

(2)
由于在系统内存中只存在三个目的,因而得以省去系统财富,对于部分必要频仍创立和销毁的目的单例格局无疑可以增加系统的品质。

(3)
允许可变数目的实例。基于单例方式大家得以开展增添,使用与单例控制相似的法子来收获内定个数的对象实例,既节省系统财富,又消除了单例单例对象共享过多有损质量的难题。

  1. 重视症结

单例形式的要害缺点如下:

(1) 由于单例形式中从未抽象层,由此单例类的增加有很大的忙绿。

(2)
单例类的职务过重,在必然水平上违反了“单一义务规范”。因为单例类既担任了工厂角色,提供了工厂方法,同时又担任了产品角色,包括部分事情方法,将成品的创立和制品的作者的效益融合到联合。

(3)
以后许多面向对象语言(如Java、C#)的周转条件都提供了机动垃圾回收的技术,由此,假使实例化的共享对象长日子不被运用,系统会认为它是污物,会自行销毁并回收财富,下次采用时又将另行实例化,这将招致共享的单例对象意况的不见。

  1. 适用场景

在以下意况下可以设想动用单例形式:

(1)
系统只要求3个实例对象,如系统须要提供七个唯一的行列号生成器或财富管理器,恐怕须要考虑能源消耗太大而只允许成立一个对象。

(2)
客户调用类的单个实例只同意接纳2个国有访问点,除了该公共访问点,不大概经过其余路线访问该实例。

思考

如何对单例格局展开改建,使得系统中有些类的目的可以存在个别两个,例如两例或三例?【注:改造之后的类可称为多例类。】

总结

第三优点
(1)
单例形式提供了对唯一实例的受控访问。因为单例类封装了它的绝无仅有实例,所以它可以严苛控制客户怎么以及曾几何时造访它。
(2)
由于在系统内存中只设有三个目的,因而可以节约系统能源,对于有个别内需反复成立和销毁的对象单例格局无疑可以提升系统的属性。
(3)
允许可变数目的实例。基于单例格局我们可以展开伸张,使用与单例控制相似的点子来博取内定个数的靶子实例,既节省系统能源,又消除了单例单例对象共享过多有损品质的标题。

主要症结
(1) 由于单例情势中一直不抽象层,因而单例类的恢宏有很大的困难。
(2)
单例类的职分过重,在一定水准上违反了“单一职责规范”。因为单例类既担任了工厂角色,提供了工厂方法,同时又担任了出品剧中人物,蕴含部分作业方法,将产品的创始和制品的自个儿的成效融合到一块儿。
(3)
以后诸多面向对象语言(如Java、C#)的周转条件都提供了自行垃圾回收的技能,因而,假诺实例化的共享对象长日子不被利用,系统会认为它是废品,会自动销毁并回收财富,下次利用时又将再也实例化,那将招致共享的单例对象景况的不见。

3.6 单例方式统计

       单例方式作为一种目的明显、结构容易、了然容易的设计形式,在软件开发中利用成效十分高,在许多使用软件和框架中都能够广泛应用。

 

1.第3优点

       单例方式的紧要优点如下:

      
(1) 单例情势提供了对唯一实例的受控访问。因为单例类封装了它的绝无仅有实例,所以它可以严峻控制客户怎么以及什么时候造访它。

      
(2) 由于在系统内存中只设有一个对象,由此可以节约系统能源,对于一些内需反复创制和销毁的对象单例格局无疑可以增强系统的性质。

      
(3) 允许可变数目标实例。基于单例方式大家得以进行增加,使用与单例控制相似的格局来拿到钦命个数的目的实例,既节约系统财富,又解决了单例单例对象共享过多有损质量的题材。

 

2.重点弱点

       单例方式的基本点弱点如下:

       (1) 由于单例情势中一贯不抽象层,因而单例类的恢宏有很大的困难。

      
(2) 单例类的任务过重,在肯定水平上违反了“单一任务规范”。因为单例类既担任了工厂角色,提供了工厂方法,同时又充当了产品角色,包括部分作业方法,将成品的始建和成品的自个儿的作用融合到手拉手。

      
(3) 以往众多面向对象语言(如Java、C#)的运营环境都提供了自行垃圾回收的技能,因而,倘使实例化的共享对象长日子不被应用,系统会认为它是废物,会自行销毁并回收能源,下次选拔时又将另行实例化,那将招致共享的单例对象情况的不见。

 

3.适用场景

       在偏下情状下得以考虑拔取单例形式:

      
(1) 系统只须要一个实例对象,如系统必要提供3个唯一的行列号生成器或财富管理器,或许必要考虑能源消耗太大而只同意创立多个目标。

      
(2) 客户调用类的单个实例只允许使用壹个共用访问点,除了该集体访问点,不大概透过其余途径访问该实例。

 

 

思考

如何对单例模式进行改造,使得系统中某个类的对象可以存在有限多个,例如两例或三例?【注:改造之后的类可称之为多例类。】

 

 

【作者:刘伟  http://blog.csdn.net/lovelion

适用场景

(1)
系统只需求一个实例对象,如系统须要提供二个唯一的种类号生成器或能源管理器,或许必要考虑能源消耗太大而只允许创制贰个对象。
(2)
客户调用类的单个实例只同意利用3个公共访问点,除了该公共访问点,不或许透过任何路线访问该实例。

相关文章