zoukankan      html  css  js  c++  java
  • 设计模式 6 —— 单件模式

    设计模式目录:

    设计模式 1 ——观察者模式

    设计模式 2 —— 装饰者模式 

    设计模式 3 —— 迭代器和组合模式(迭代器)

    设计模式 4 —— 迭代器和组合模式(组合)

    设计模式 5 —— 工厂模式

    设计模式 6 —— 单件模式

    设计模式 7 —— 命令模式

    设计模式 8 —— 适配器和外观模式

    设计模式 9 —— 模板方法模式

    设计模式 10 —— 状态模式

    概述:

    第1 部分 问题引入

    第2 部分 模式定义和实现

    第3 部分 示例

    第1 部分 问题引入

      有时候某些对象我们只需要一个,如:线程池、缓存、对话框等等,对于这类对象我们只能有一个实例,如果我们制造出多个实例,就会导致很多问题产生。

    但是我们怎样才能保证一个类只有一个实例并且能够便于访问?这里我们想到了全局变量,全局变量确实是可以保证该类可以随时访问,但是它很难解决只有一个实例问题。最好的办法就是让该自身来负责保存它的唯一实例。这个类必须要保证没有其他类来创建它。这里我们可以将其构造方法私有化。即

    Public MyClass{
             PrivateMyClass(){}
    }

    含有私有化构造器的类就能保证它不能被其他类实例化了。但是我们如何来获取这个实例化类呢?提供一个方法用于返回该类的实例对象即可实现。

    public class MyClass {
        private MyClass(){
            
        }
        
        public static MyClass getInstance(){
            return new MyClass();
        }
    }

    第2 部分 模式定义和实现

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

    从上面可以看出单例模式有如下几个特点:1、它只有一个实例。 2、它必须要自行实例化。3、它必须自行想整个系统提供访问点。

    模式结构:

    模式实现:

     1 package firsthead.singleton;
     2 
     3 /**
     4  * 
     5  * @ClassName: Singleton
     6  * TODO
     7  * @author Xingle
     8  * @date 2014-8-29 上午9:21:56
     9  */
    10 
    11 //NOTE: This is not thread safe!
    12 public class Singleton {
    13     
    14     private static Singleton uniqueInstance;
    15     
    16     //这里是其他有用的实例化变量
    17     
    18     private Singleton() {}
    19     
    20     public static Singleton getInstance(){
    21         if(uniqueInstance == null){
    22             uniqueInstance = new Singleton();
    23         }        
    24         return uniqueInstance;        
    25     }
    26     
    27     //这里是其他有用的方法
    28 
    29 }

     在《Head  First》有这样一个场景,就是说有两个线程都要执行这段代码,很有可能会产生两个实例对象。如下图:

    处理多线程,只要把getInstance() 变成同步(synchronized )方法,多线程问题就几乎可以轻易地解决了:

     1 //NOTE: This is not thread safe!
     2 public class Singleton {
     3     
     4     private static Singleton uniqueInstance;
     5     
     6     //这里是其他有用的实例化变量
     7     
     8     private Singleton() {}
     9     
    10     public static synchronized Singleton getInstance(){
    11         if(uniqueInstance == null){
    12             uniqueInstance = new Singleton();
    13         }        
    14         return uniqueInstance;        
    15     }
    16     
    17     //这里是其他有用的方法
    18 
    19 }

      但是,同步会减低性能。其实只有第一次执行此方法时,才真正需要同步。换句话说,一旦设置好uniqueInstance 变量,就不需要同步这个方法了,之后每次调用这个方法,同步都是一种累赘。

    为了要符合大多数Java应用程序,需要确保单件模式能再多线程的状况下正常工作。但是同步getInstance() 的做法将拖垮性能,该怎么办呢?

    这里有一些选择:

    1.如果getInstance() 的性能对应用程序不是很关键,就什么都别做。

    同步getInstance() 的方法既简单又有效,但是同步一个方法可能造成程序执行效率下降100倍。因此,如果将getInstance() 的程序使用在频繁运行的地方,你可能就要重新考虑了。

    2. 使用“急切”创建实例,而不用延迟实例化的做法

    如果应用程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,你可能想要急切创建此单件,如下:

     1 public class Singleton {
     2     
     3     //在静态初始化器中创建单件。这段代码保证了线程安全
     4     private static Singleton uniqueInstance = new Singleton();
     5      
     6     private Singleton() {}
     7     
     8     public static Singleton getInstance() {
     9         //已经有实例了,直接使用它
    10         return uniqueInstance;
    11     }
    12 }

    利用这个做法,依赖JVM在加载这个类时马上创建此唯一的单件实例。JVM保证在任何线程访问uniqueInstance 静态变量之前,一定先创建此实例。

    3.用“双重检查加锁”,在getInstance()中减少使用同步

    利用双重检查加锁,首先检查是否实例已经创建了,如果尚未创建,“才”进行同步。这样,只有第一次会同步,这正是我们想要的。

     1 public class Singleton {
     2     
     3     //volatile 关键词确保,当uniqueInstance 变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance
     4     private volatile static Singleton uniqueInstance;
     5      
     6     private Singleton() {}
     7  
     8     public static Singleton getInstance() {
     9         //检查实例,如果不存在,就进入同步区块
    10         //只有第一次才彻底执行这里的代码
    11         if (uniqueInstance == null) {
    12             synchronized (Singleton.class) {
    13                 //进入区块后,再检查一次。如果仍为null,才创建实例
    14                 if (uniqueInstance == null) {
    15                     uniqueInstance = new Singleton();
    16                 }
    17             }
    18         }
    19         return uniqueInstance;
    20     }
    21 
    22 }

    注意:双重检查加锁不适用于1.4及更早版本的java

    第3 部分 示例

    下面代码给出一个实例

     Singleton.java

     1 package firsthead.singleton;
     2 
     3 /**
     4  * 
     5  * @ClassName: Singleton
     6  * TODO
     7  * @author Xingle
     8  * @date 2014-8-29 上午9:21:56
     9  */
    10 public class Singleton {
    11     
    12     protected static Singleton uniqueInstance;
    13      
    14     // other useful instance variables here
    15  
    16     protected Singleton() {}
    17  
    18     public static synchronized Singleton getInstance() {
    19         if (uniqueInstance == null) {
    20             uniqueInstance = new Singleton();
    21         }
    22         return uniqueInstance;
    23     }
    24  
    25     // other useful methods here
    26 }

    CoolerSingleton.java

     1 public class CoolerSingleton extends Singleton{
     2     // useful instance variables here
     3     protected static Singleton uniqueInstance;
     4      
     5     private CoolerSingleton() {
     6         super();
     7     }
     8      
     9     // useful methods here
    10 }

    HotterSingleton.java

     1 public class HotterSingleton extends Singleton{
     2     
     3     // useful instance variables here
     4     
     5     private HotterSingleton(){
     6         super();
     7     }
     8     
     9     // useful methods here
    10 }

    SingletonTestDrive.java

     1 public class SingletonTestDrive {
     2     
     3     public static void main(String[] args){
     4         Singleton foo = CoolerSingleton.getInstance();
     5         Singleton bar = HotterSingleton.getInstance();
     6         
     7         System.out.println(foo);
     8         System.out.println(bar);
     9     }
    10 
    11 }

    结果:

  • 相关阅读:
    Hbuilder——报错The keyword 'export' is reserved
    控制器里路径变量的使用
    Spring 控制器重定向
    Spring A 标签链接使用
    Spring switch的使用
    thymeleaf如何遍历数据 each循环的使用
    spring 机制 扫描包
    Spring分层次建包
    什么是MVC模型
    如何运行spring boot 工程
  • 原文地址:https://www.cnblogs.com/xingele0917/p/3941750.html
Copyright © 2011-2022 走看看