zoukankan      html  css  js  c++  java
  • 设计模式-单例模式

    单例模式

    概念

    确保一个类只有一个实例,并提供一个全局访问点。

    UML类图

    UML类图说明

    1)getInstance()是一个静态方法,它是一个全局访问点,我们可以通过Singleton.getInstance()来访问它,

    这和访问全局变量一样简单,只是多了一个优点:单例模式可以延迟实例化

    2)类变量uniqueInstance用来指向唯一的类实例

    单例模式的实现

    分析:

      依靠程序之间的约定或利用全局变量

      比如利用Java的静态变量

            V

      单例模式经过了实践的考验,可确保只有一个实例被创建,

      也给了我们一个全局访问点,优于程序员之间的约定和全局变量,

      并且没有全局变量的缺点

            V

      全局变量的缺点:1)污染命名空间

              2)在程序一开始就要创建对象,如果这个对象非常消耗资源,而程序的这次执行又没用到它,

              就会造成浪费,单例模式可以在需要时创建。

    单件模式的实现分析

      一个类如果不是私有的,那么我们可以多次实例化它

                V

      如果把一个类(MyClass)的构造函数私有化,那么只有这个类(MyClass)内的代码才能

      调用此构造器

                V

      必须有MyClass的实例才能调用MyClass构造器,没有其他类能够实例化MyClass,

      这是个鸡生蛋蛋生鸡的问题

                V

      提供一个静态方法,通过类来调用,比如MyClass.getInstance()

                V

      你能够确保MyClass只能产生一个实例吗?答案是否定的

    说明

    1)我们把某个类设计成自己管理的一个单独实例,同时也避免了其他类再自行产生实例,

    想要取得单例实例,通过单例类是唯一的途径

    2)我们提供了对这个实例的全局访问点,当你需要实例时,向类查询,它会返回单个实例,

    利用延迟实例化的方式创建单例对资源敏感的对象特别重要

    单例模式基本实现

    Singleton.java

    package com.java.singleton;
    
    public class Singleton {
        private static Singleton uniqueInstance = null;
        
        private Singleton(){}
        
        public static Singleton getInstance(){
            if (uniqueInstance == null) {
                uniqueInstance = new Singleton();
            }
            return uniqueInstance;
        }
    }
    View Code

    案例

    ChocOHolic公司有工业强度巧克力锅炉控制器,锅炉负责把巧克力和牛奶融在一起,

    然后送到下一个阶段,以制造巧克力棒,代码如下

    ChocolateBoiler.java

     1 package com.design.singleton;
     2 
     3 public class ChocolateBoiler {
     4     private boolean empty;
     5     private boolean boiled;
     6     
     7     //初始化时,锅炉室空的
     8     public ChocolateBoiler() {
     9         empty = true;
    10         boiled = false;
    11     }
    12     
    13     public boolean isEmpty() {
    14         return empty;
    15     }
    16     
    17     public boolean isBoiled() {
    18         return boiled;
    19     }
    20     
    21     //往锅炉填入原料时,锅炉必须是空的
    22     public void fill() {
    23         if (isEmpty()) {
    24             empty = false;
    25             boiled = false;
    26             //填入巧克力和牛奶的混合物
    27         }
    28     }
    29     
    30     //锅炉排出时,必须是满的并且是被煮过的
    31     public void drain() {
    32         if(!isEmpty() && isBoiled()) {
    33             //排出煮沸的巧克力和牛奶
    34             empty = true;
    35         }
    36     }
    37     
    38     //煮混合物时,锅炉必须是满的并且是没有被煮过的
    39     public void boil() {
    40         if (!isEmpty() && !isBoiled()) {
    41             //将锅炉内的混合物煮沸
    42             boiled = true;
    43         }
    44     }
    45 }
    View Code

    需求:公司担心同时有两个或两个以上的锅炉实例存在,可能会发生很糟糕的事,要求锅炉只能有一个实例

    代码实现

    ChocolateBoiler.java

     1 package com.design.singleton;
     2 
     3 public class ChocolateBoiler {
     4     private boolean empty;
     5     private boolean boiled;
     6     
     7     //初始化时,锅炉室空的
     8 //    public ChocolateBoiler() {
     9 //        empty = true;
    10 //        boiled = false;
    11 //    }
    12     public static ChocolateBoiler chocolateBoiler = null;
    13     
    14     public static ChocolateBoiler getInstance() {
    15         if (chocolateBoiler == null) {
    16             chocolateBoiler = new ChocolateBoiler();
    17         }
    18         return chocolateBoiler;
    19     }
    20     
    21     private ChocolateBoiler() {
    22         empty = true;
    23         boiled = false;
    24     }
    25     
    26     
    27     public boolean isEmpty() {
    28         return empty;
    29     }
    30     
    31     public boolean isBoiled() {
    32         return boiled;
    33     }
    34     
    35     //往锅炉填入原料时,锅炉必须是空的
    36     public void fill() {
    37         if (isEmpty()) {
    38             empty = false;
    39             boiled = false;
    40             //填入巧克力和牛奶的混合物
    41         }
    42     }
    43     
    44     //锅炉排出时,必须是满的并且是被煮过的
    45     public void drain() {
    46         if(!isEmpty() && isBoiled()) {
    47             //排出煮沸的巧克力和牛奶
    48             empty = true;
    49         }
    50     }
    51     
    52     //煮混合物时,锅炉必须是满的并且是没有被煮过的
    53     public void boil() {
    54         if (!isEmpty() && !isBoiled()) {
    55             //将锅炉内的混合物煮沸
    56             boiled = true;
    57         }
    58     }
    59 }
    View Code

    问题:在用多线程改进代码时,发现fill()方法竟然允许在加热的过程中继续加入原料

    分析

      通过增加synchronized关键字到getInstance()方法中,

      迫使每个线程在进入这个方法之前,要先等候别的线程离开这个方法

                   V

      但是同步会降低性能,并且只有第一次执行方法时才真正需要同步,

      之后每次调用这个方法,同步都是一种累赘,同步一个方法可能造成

      程序执行效率下降100

                   V

      如果你的程序可以接受getInstance()造成的额外负担,那就忘了这件事吧

                   V

      如果getInstance()方法调用比较繁琐,我们可以使用急切的创建实例,

      而不用延迟实例化的做法

                   V

      依赖JVM在加载这个类时马上创建此类唯一的实例,JVM保证在任何线程访问唯一静态实变量之前,

      先创建此实例

                   V

      利用双重检查加锁,首先检查实例是已经被创建,如果未被创建,才进行同步,

      如果创建了,则不进行同步,这正是我们想要的:只有第一次才会同步。

    synchronized实现

     1 package com.design.singleton;
     2 
     3 public class Singleton {
     4     public static Singleton instance = null;
     5     
     6     private Singleton() {}
     7     
     8     public static synchronized Singleton getInstance() {
     9         if (instance == null) {
    10             instance = new Singleton();
    11         }
    12         return instance;
    13     }
    14 }
    View Code

    急切创建实例实现

     1 package com.design.singleton;
     2 
     3 public class Singleton {
     4     public static Singleton instance = new Singleton();
     5     
     6     private Singleton() {}
     7     
     8     public static synchronized Singleton getInstance() {
     9         return instance;
    10     }
    11 }
    View Code

    双重检查加锁实现

     1 package com.design.singleton;
     2 
     3 public class Singleton {
     4     public volatile static Singleton instance;
     5     
     6     private Singleton() {}
     7     
     8     public static Singleton getInstance() {
     9         if (instance == null) {
    10             synchronized (Singleton.class) {
    11                 if (instance == null) {
    12                     instance = new Singleton();
    13                 }
    14             }
    15         }
    16         return instance;
    17     }
    18 }
    View Code

                

    应用场景

    对于一些对象,我们只需要一个即可,比如,线程池、对话框、日志对象等,这类对象只能有一个实例,

    如果制造多个实例,会产生许多问题,比如,程序的行为异常,资源使用过量或是结果不一致

    比如,注册表设置的对象,你不希望这样的对象有多分拷贝吧,这样会把设置搞得一塌糊涂,

    单例模式可以确保程序中使用的全局资源只有一份。

  • 相关阅读:
    虚拟机Centos安装docker小记
    Python selenium入门
    selenium Error
    DveOps路线指南
    DevOps
    Go语言常量和变量
    安装Go语言及环境的搭建
    Win10 搭建IIS服务
    linux 上搭建sftp服务
    linux小命令
  • 原文地址:https://www.cnblogs.com/marton/p/11553632.html
Copyright © 2011-2022 走看看