zoukankan      html  css  js  c++  java
  • [设计模式]单件模式概念和三个解决多线程问题的方案

    主要是从 Head Fisrt 设计模式中学习到知识;

    1. 定义单件模式

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

      在整个系统上下文中,只有一个对象,对于很多在系统中只需要一个或者创建代价比较大的对象,可以使用,例如:线程池、缓存、对话框、处理偏好设置和注册表对象、日志对象、充当打印机、显卡等设备的驱动程序的对象;

      采用单件模式可以避免系统维护太多没有记录状态数据、所有实例功能可替代的相关对象,还有就是多个实例处理会可能会造成结果不一致的问题;

    2. 主要思想

      2.1 由类持有一个静态的类对象,并通过方法暴露对类对象的获取接口;

      2.2 类禁止外部对象实例化

    3. 单件模式的实现

      3.1 下面是单件模式的简单实现,思想就是持有一个静态对象,当需要的时候就通过类的公用方法获取对象的引用,这里还有一个做法就是延迟实例化,当对象需要用到的时候才会去实例化对象,如果对象没有被引用到,那么就是一直都不会实例化,系统资源也不会浪费。

    public class Singleton {
      private static Singleton uniqueInstance;
    
      private Singleton () {}
    
      public static Singleton getInstance () {
        if (uniqueInstance == null) {
              uniqueInstance = new Singleton();
          }
        }
        return uniqueInstance;
      }
    
      public static void main(String[] args) {
    
        Singleton singleton = Singleton.getInstance();  
        System.out.println(singleton.toString());    // Singleton@15db9742
    
        singleton = Singleton.getInstance();
        System.out.println(singleton.toString());    // Singleton@15db9742
    
        singleton = Singleton.getInstance();
        System.out.println(singleton.toString());    // Singleton@15db9742
    
        singleton = Singleton.getInstance();
        System.out.println(singleton.toString());    // Singleton@15db9742
    
      }
    }

      这里打印出来,几个类的对象的内存地址都是同一个,引用了同一个对象。

    4. 单件模式和多线程

      上面的单件模式实现已经能够满足基本的使用要求,但是当单件模式模式遇到多线程之后,很多奇怪的问题就发生了(事实上很多代码遇到多线程后都会有问题),

      例如上面的例子,当代码的推进状态像下面的状态,就会出现问题:

      这个时候对象已经不是同一个了,多线程造成 单件模式 已经和我们定义的不一样了。

      为了解决这个问题,有以下的三个解决方法:

      * 使用synchronized方法,将getInstance()变成同步的方法,这样能够解决我们的问题,但是实例化只是在第一次的时候使用,后面就没有这个问题存在了,然后通过synchronized强制同步会降低系统的并发性能,这种情况适合getInstance()的性能不影响或者影响了可以接受的地方;

      

     public static synchronized Singleton getInstance () {
    ...

      * 使用“急切”的实例化方法,在类初始化的时候就像对象生成;

    public class Singleton {
      private static Singleton uniqueInstance = new Singleton();
      private Singleton () {}
      public static Singleton getInstance () { 
        return uniqueInstance;  
      }
    }

      * 使用“双重检查加锁”( since jdk 1.4 )

      使用之后代码是这样子的:

      

    public class Singleton {
      private volatile static Singleton uniqueInstance;
    
      private Singleton () {}
    
      public static Singleton getInstance () {
        if (uniqueInstance == null) {
          synchronized (Singleton.class) {
            if (uniqueInstance == null){
                  uniqueInstance = new Singleton();
            }
          }
        }
        return uniqueInstance;
      }
    
      public static void main(String[] args) {
        System.out.println("test");
        Singleton singleton = Singleton.getInstance();
        System.out.println(singleton.toString());
    
        singleton = Singleton.getInstance();
        System.out.println(singleton.toString());
        singleton = Singleton.getInstance();
        System.out.println(singleton.toString());
        singleton = Singleton.getInstance();
        System.out.println(singleton.toString());
      }
    }

      通过 volatile 标识该变量的修改是对其他线程可见的,还有禁止指令排序;详细用法见:地址

      然后当判断是未初始化的时候,再将类进入锁定然后再次判空之后再初始化类,这样就解决了我们前面遇到的问题,也很好的实现了单件模式的理念。

  • 相关阅读:
    ROW_NUMBER()用法(转)
    winform MD5值生成
    VC使用“添加方法向导”添加调度映射方法“
    MyGeneration配置说明
    dataGridView取消自动生成列
    PHP魔术常量(magic constant)
    Eclipse添加DTD文件实现xml的自动提示功能
    Google:5个常见的SEO错误和6个SEO好主意
    PHP检查PEAR是否工作
    手把手教你在ubuntu上安装LAMP
  • 原文地址:https://www.cnblogs.com/zhuangmingnan/p/9383859.html
Copyright © 2011-2022 走看看