zoukankan      html  css  js  c++  java
  • 设计模式解密(1)- 单例模式

    1、前言

    1-1、 概述

           设计模式 = 某类特定问题的解决方案,那么单例模式是解决什么问题的解决方案呢?

      定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

      含义:单例  =  一个实例

      解决的问题:在任何时间内只有一个类实例存在的模式

      解决方法:保证一个类只有一个实例化对象,并提供一个全局访问入口

      本质:控制实例的数量

      注意:要合理的使用单例,避免单例成为瓶颈

      英文:Singleton

        类型:创建类模式

    1-2、问题引入

    模拟网站访问数量统计功能:

    package com.designmode.singleton;
    
    /**
     * 网站计数器
     */
    class WebCounter {
        private int count = 0;
    
    	public int getCount() {
    		return count;
    	}
    	public void setCount(int count) {
    		this.count = count;
    	}
    }
    
    /**
     * 用户访问
     */
    class Visitor{
    	public WebCounter webCounter;
    	public Visitor(WebCounter mwebCounter){
    		webCounter = mwebCounter;
    	}
    	//访问
    	public void visit(){
    		webCounter.setCount(webCounter.getCount()+1);;
    	}
    }
    /**
     * 模拟用户访问网站
     */
    public class SingleTest{
    	public static void main(String[] args){
    		WebCounter webCounter1 = new WebCounter();
    		WebCounter webCounter2 = new WebCounter();
    		Visitor visitor1 = new Visitor(webCounter1);
    		Visitor visitor2 = new Visitor(webCounter2);
    
            System.out.println("是不是同一个网站?");
            if(webCounter1.equals(webCounter2)){
                System.out.println("是");
            }else {
                System.out.println("不是");
            }
            //visitor1访问该网站
            visitor1.visit();
            System.out.println("访问量:" + webCounter1.getCount());
            //visitor2访问该网站
            visitor2.visit();
            System.out.println("访问量:" + webCounter2.getCount());
        }
    }
    

    结果:

    是不是同一个网站?
    不是
    访问量:1
    访问量:1
    

    从结果看,两个人访问的不是一个网站实例,其实我们要实现的逻辑是,访问同一个网站,计算访问量,这显然是不符合我们想要的

    2、介绍

     2-1、分析引入的问题

      冲突:从上面的结果可以看出,网站计数器类操作的明显不是同一个实例

      目标:所有访问者操作同一个网站计数器类

      单例模式就是为了解决这类问题的解决方案:实现一个类只有一个实例化对象,并提供一个全局访问入口

     2-2、解决引入的问题

    解决:改造一下网站计数器类的实现代码

    package com.designmode.singleton;
    
    /**
     * 网站计数器
     */
    class WebCounter {
        private int count = 0;
       
        private static WebCounter instance  = new  WebCounter();
        private WebCounter() {
        }
        public static WebCounter getInstance() {
           return instance;
        }
        
      public int getCount() {
    	return count;
      }
      public void setCount(int count) {
    	this.count = count;
      }
    }
    /**
     * 用户访问
     */
    class Visitor{
    	public WebCounter webCounter;
    	public Visitor(WebCounter mwebCounter){
    		webCounter = mwebCounter;
    	}
    	//访问
    	public void visit(){
    		webCounter.setCount(webCounter.getCount()+1);;
    	}
    }
    /**
     * 模拟用户访问网站
     */
    public class SingleTest{
    	public static void main(String[] args){
    		WebCounter webCounter1 = WebCounter.getInstance();
    		WebCounter webCounter2 = WebCounter.getInstance();
    		Visitor visitor1 = new Visitor(webCounter1);
    		Visitor visitor2 = new Visitor(webCounter2);
    		
            System.out.println("是不是同一个网站?");
            if(webCounter1.equals(webCounter2)){
                System.out.println("是");
            }else {
                System.out.println("不是");
            }
            //visitor1访问该网站
            visitor1.visit();
            System.out.println("访问量:" + webCounter1.getCount());
            //visitor2访问该网站
            visitor2.visit();
            System.out.println("访问量:" + webCounter2.getCount());
        }
    }
    

    再来看一下结果:

    是不是同一个网站?
    是
    访问量:1
    访问量:2
    

    这次是对的!!!

     2-3、实现原理

    引入单例模式:一般实现方式

    public class Singleton {
    	//1. 创建私有变量 instance(用以记录 Singleton 的唯一实例)
    	//2. 内部进行实例化
        private static Singleton instance  = new  Singleton();
    	//3. 把类的构造方法私有化,不让外部调用构造方法实例化
        private Singleton() {
        }
    	//4. 定义公有方法提供该类的全局唯一访问点
    	//5. 外部通过调用getInstance()方法来返回唯一的实例
        public static Singleton getInstance() {
            return instance;
        }
    }

     

    2-4、优点、缺点

    优点:
      1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就防止其它对象对自己的实例化,确保所有的对象都访问一个实例
      2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类在实例化进程上有相应的伸缩性
      3.提供了对唯一实例的访问入口
      4.由于在系统内存中只存在一个对象,因此可以节约系统资源,当需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能
      5.允许可变数目的实例(可以根据实际情况需要,在单例模式的基础上扩展做出双例模式、多例模式)
      6.避免对共享资源的多重占用
    缺点:
      1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态
      2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
      3.单例类的职责过重,在一定程度上违背了“单一职责原则”
      4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失

    3、实现

    3-1、饿汉模式、懒汉模式

    饿汉模式:

    package com.designmode;
    
    /**
     * 饿汉模式(最简单的形式)
     */
    public class Singleton {
        private static  Singleton instance  = new  Singleton();
    
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            return instance;
        }
    }
    

    应用场景:

      要求直接在应用启动时加载并初始化
      单例对象要求初始化速度非常快

    懒汉模式:

    package com.designmode;
    
    /**
     * 懒汉模式(最简单的形式)
     */
    public class Singleton {
    	private static  Singleton instance  = null;
    
        private Singleton() {
        }
    
        public static Singleton newInstance() {
    	    if(instance == null){
    	    	instance = new Singleton();
    	    }
            return instance;
        }
    }
    

     应用场景:

      单例初始化的操作耗时比较长(可以相应缩短应用启动时间)
      单例只是在某个特定场景的情况下才会被使用,即按需延迟加载单例

    对比:

      饿汉式:自动进行单例的初始化
      懒汉式:有需要的时候才手动调用getInstance()进行单例的初始化操作

    3-2、多线程下的实现

    在多线程的情况下:

      “饿汉式单例模式”:适用,因为JVM只会加载一次单例类
      “懒汉式单例模式”:不适用,因为“懒汉式”在创建单例时是线程不安全的,多个线程可能会并发调用 getInstance 方法从而出现重复创建单例对象的问题

    下面有几个解决方案:

    方案1:同步锁

    //使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成instance被多次实例化
    package com.designmode;
    
    public class Singleton {
    	private static  Singleton instance  = null;
    
        private Singleton() {
        }
    
        public static  Singleton getInstance() {
        	synchronized (Singleton.class){
               if(instance == null){
            	  instance = new Singleton();
               }
        	}
          return instance;
       }
    }
    

      

    方案2:双重校验锁

    //在同步锁的基础上( synchronized (Singleton.class) 外)添加了一层if,这是为了在Instance已经实例化后下次进入不必执行 synchronized (Singleton.class) 获取对象锁,从而提高性能
    package com.designmode;
    
    public class Singleton {
    	private static  Singleton instance  = null;
    
        private Singleton() {
        }
    
        public static  Singleton getInstance() {
        	if(instance == null){
        		synchronized (Singleton.class){
        			if(instance == null){
        				instance = new Singleton();
        			}
        		}
        	}
            return instance;
       }
    }
    

      

    方案3:静态内部类

    //在JVM进行类加载的时候会保证数据是同步的,我们采用内部类实现:在内部类里面去创建对象实例
    //只要应用中不使用内部类 JVM 就不会去加载这个单例类,也就不会创建单例对象,从而实现“懒汉式”的延迟加载和线程安全。
    package com.designmode;
    
    public class Singleton {
    	//在装载该内部类时才会去创建单例对象
        private static class Singleton2{
        	private static Singleton instance  = new Singleton();
        }
        
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            return Singleton2.instance;
        }
    }
    

      

    方案4:枚举类型

    //最简洁、易用的单例实现方式,(《Effective Java》推荐)
    package com.designmode;
    
    public enum Singleton{
        //定义一个枚举的元素,它就是Singleton的一个实例
        INSTANCE;
        private Singleton() {
          
        }
    	public void doSomething(){  
            
        }  
    }
    

    调用方式:

    Singleton.INSTANCE.doSomething();
    

      

    4、总结

          设计模式 = 某类特定问题的解决方案,那么单例模式是解决什么问题的解决方案呢?

      含义:单例  =  一个实例

      解决的问题:在任何时间内只有一个类实例存在的模式

      解决方法:保证一个类只有一个实例化对象,并提供一个全局访问入口

      本质:控制实例的数量

      注意:要合理的使用单例,避免单例成为瓶颈

    PS:源码地址   https://github.com/JsonShare/DesignPattern/tree/master

    PS:原文地址 http://www.cnblogs.com/JsonShare/p/7093947.html

       

  • 相关阅读:
    poj3720
    poj3099
    poj3734
    poj3112
    poj3723
    十二个开源UML工具推荐
    关于大型asp.net应用系统的架构—如何做到高性能高可伸缩性
    读《中央确定西部新十年战略》有感
    DirectShow基础编程 最简单的源Filter的编写步骤
    Access2007无法执行查询,操作或事件已被禁用模式阻止
  • 原文地址:https://www.cnblogs.com/JsonShare/p/7093947.html
Copyright © 2011-2022 走看看