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

    一、单例模式概述

    1. 定义:确保一个类只有一个实例,并提供一个全局访问点来访问这个实例

    简单的说,就是你有且只有一个女朋友(有多个女朋友的模式不是这里~~),并且你的女朋友很特殊,从来只听你的话,所以别人想和她交流(访问她)就必须通过你(全局访问点)来和她交流。

    运用:系统中用到单例模式的地方很多,比如Windows系统点击开始只能出现一个开始界面,Ctrl+Alt+T, 只能出现一个资源管理器,每个进程有且对应唯一一个进程ID等等

    单例模式是为了让资源得到最大化利用,不浪费资源

    同时假如不采用此模式,就可能在不同时刻打开同一个界面,但界面中的内容又各不相同,因而用户极易产生误解,影响使用效率。因此单例模式在系统中的应用非常重要

    2. 要点

    1)某一个单例类只能有一个实例

    2)必须自行创建这个实例

    3)必须向系统提供这个实例

    二、单例模式的结构和实现

    1. 结构:

    单例模式关键要素:

    1)私有的静态变量

    2)私有构造函数

    3)公有的静态方法

    4)根据变量数判断是否生成女朋友(唯一的对象)

    5)公有静态方法调用私有构造方法

    关键词释义:

    1)Singleton(单例):在单例类的内部创建它的唯一实例

    2)Getinstance(静态方法):通过它产生唯一实例

    3)instance(静态变量):判断是否可以产生实例

    2. 实现:

    /**
     * @author ***
     * @title: Singleton
     * @projectName design-pattern
     * @description TODO
     * @date 2021/7/5 15:24
     */
    public class Singleton {
    
       /**
        * 静态私有成员变量
        */
        private static Singleton instance = null;
    
    
        /**
         * 私有构造函数
         */
        private Singleton() {
            System.out.println("恭喜你,获得一个女朋友~~");
        }
    
    
        /**
         * 静态公有方法,返回实例
         *
         * @return
         */
        public static Singleton getInstance() {
            //没女朋友
            if (instance == null) {
                //生成一个吧
                instance = new Singleton();
            }
            //有就返回当前的,
            return instance;
        }
    }
    /**
     * @author ***
     * @title: Program
     * @projectName design-pattern
     * @description TODO
     * @date 2021/7/5 15:24
     */
    public class Program {
        public static void main(String[] args) {
            Singleton s1 = Singleton.getInstance();
            Singleton s2 = Singleton.getInstance();
            if (s1 == s2) {
                System.out.println("怎么能想要共有一个女朋友呢?S2 赶紧换一个吧...");
            }
        }
    }

    控制台输出:

    三、饿汉式和懒汉式单例

    1. 饿汉

    如名字一般,就是出于很饥饿的状态,所以在定义静态变量时就实例化了单例

    /**
     * @author ***
     * @title: HungryManSingleton
     * @projectName design-pattern
     * @description TODO
     * @date 2021/7/5 15:37
     */
    public class HungryManSingleton {
    
        //静态变量实例化单例类
        private static HungryManSingleton instance = new HungryManSingleton();
    
        private HungryManSingleton() {
        }
    
        public static HungryManSingleton getInstance() {
            return instance;
        }
    }

    2. 懒汉

    懒汉式单例类则是在类第一次被引用时将自己实例化,单例类被加载时不会实例化,所以这很符合懒汉的气质。

    但是在这里要注意的是,在定义静态变量时没有实例化单例类,而是在第一次调用静态方法时实例化单例类,这就会产生问题,高并发,多线程实现懒汉式单例时会创建多个对象,从而违背了单例模式的设计意图。

    也就是还是要对女朋友的个数进行判断。这要怎么办呢?

    在多线程的情况下,就要对该代码段进行控制,即每次只让一个线程进入并创建实例,也就是相当于现在的“共享女友”,帮你拍照啊,陪你去看电影啊 ,巴拉巴拉

    但是,该“共享女友”有且只有一个,即单例类的唯一实例。所以,土豪们(各个线程)得一个一个租用,上一个用完了下一个才能租用。因此代码如下:

    /**
     * @author ***
     * @title: LazyManSingleton
     * @projectName design-pattern
     * @description TODO
     * @date 2021/7/5 15:56
     */
    public class LazyManSingleton {
        private static LazyManSingleton instance = null;
        //看做一个门
        private static final Object root = new Object();
        
        //程序运行时创建只读辅助对象
        private LazyManSingleton() {
    
        }
    
        public static LazyManSingleton getInstance() {
            //在房间外问:房间里有人吗 ? 没人回应 ,可能没,可能下一秒有人进去 ,我却以为没人
            if (instance == null) {
                //第二次判断 
                //把门关了,外面线程进不来,只能里面的出来,外面的才能进
                synchronized (root){
                    //继续问,房间里有人吗?  有就真的有,没有就真的没
                    if (instance == null) {
                        //创建实例
                        instance = new LazyManSingleton();
                    }
                }
            }
            return instance;
        }
    
    }

    二者比较:

    1)饿汉式单例

    优点:无需考虑多线程同时访问的问题,确保实例唯一性。调用速度和反应时间快于懒汉模式,因为饿汉一开始就创建,后面则直接拿来用就可以了。

    缺点:不管单例对象是否需要,都会在类加载时创建,这样不如懒汉式单例,资源利用不高,且加载时间较长。如启动VS,Eclipse等,需要loading许多可能要的可能不要的,要等啊...

    2)懒汉式单例

    优点:第一次使用时创建,不会一直占用资源,即延迟加载。

    缺点:必须考虑多线程问题,特别是单例类作为资源控制器时,会涉及资源初始化,也会耗费许多时间,也会出现多线程同时首次引用此类,造成拥堵,导致系能性能降低

    四、单例模式的优缺点和适用环境

    1.  单例模式的优点

    1)提供唯一实例的受控访问,可以严格控制何时访问

    2)由于只存在一个对象,可以节省系统资源

    3)如果将单例模式的实例数目变为可变,即将单例模式进行扩展,即可获得指定数目的实例对象,即节省资源,又提高效率(相当于多例类)

    2. 单例模式的缺点

    1)没有抽象层,扩展有较大困难

    2)单例类职责过重,一定程度上违背了单一职责原则(单例类即提供业务方法,又提供了创建对象的方法(工厂方法),对象创建和对象本身耦合在了一起)

    3)C#、JAVA 拥有GC(自动垃圾回收机制) (可以去了解下) ,在实例化的对象长时间不被利用,会被误认为垃圾进行自动销毁,下次利用又要重新创建,导致共享的单例对象丢失(女朋友丢了可不好受啊..)

    3. 单例模式的适用环境

    1)在系统只要一个实例对象(man只要一个girlfriend)/(资源管理器).....

    2)客户调用类的单个实例只允许使用一个公共访问点,除了该点外,不允许其他途径来访问实例

    五、其他写法

    1.  饿汉式代码

    /**
     * @author ***
     * @title: SingletonDemo1
     * @projectName design-pattern
     * @description 单例模式:饿汉式
     * @date 2021/7/5 16:41
     */
    public class SingletonDemo1 {
        //类初始化立即加载,由于类加载时完成初始化,线程安全。不用同步快,调用效率高。
        private static SingletonDemo1 instance = new SingletonDemo1();
    
        private SingletonDemo1() {
    
        }
    
        public static SingletonDemo1 getInstance() {
            return instance;
        }
    }

    2. 懒汉式 同步方法

    /**
     * @author ***
     * @title: SingletonDemo2
     * @projectName design-pattern
     * @description 单例模式:懒汉式线程安全
     * @date 2021/7/5 16:42
     */
    public class SingletonDemo2 {
    
            //类初始化延迟加载,方法同步线程安全,调用效率低。
            private static SingletonDemo2 instance = null;
    
            private SingletonDemo2() {
            }
    
            public static synchronized SingletonDemo2 getInstance() {
    
                if (instance == null) {
                    instance = new SingletonDemo2();
                }
                return instance;
            }
    }

    3. 懒汉式 同步方法

    /**
     * @author ***
     * @title: SingletonDemo3
     * @projectName design-pattern
     * @description 单例模式:懒汉式双重检查锁
     * @date 2021/7/5 16:44
     */
    public class SingletonDemo3 {
        //由于编译器优化和JVM内部模型原因,同步块会有问题,会出现问题
        private static SingletonDemo3 instance = null;
    
        private SingletonDemo3() {
    
        }
    
        public static SingletonDemo3 getInstance() {
    
            if (instance == null) {
                synchronized (SingletonDemo3.class) {
                    if (instance == null) {
                        instance = new SingletonDemo3();
                    }
                }
    
            }
            return instance;
        }
    }

    4 .静态内部类

    /**
     * @author ***
     * @title: SingletonDemo4
     * @projectName design-pattern
     * @description 单例模式:静态内部类懒加载
     * @date 2021/7/5 16:45
     */
    public class SingletonDemo4 {
        //外部没有static属性,不会立即加载对象
        //真正调用getInstance,才会加载内部类
        //高效+安全+延迟加载
        private static class SingletonInstance {
            public static SingletonDemo4 instance = new SingletonDemo4();
        }
    
        private SingletonDemo4() {
    
        }
    
        public static SingletonDemo4 getInstance() {
            return SingletonInstance.instance;
        }
    }

    5.  枚举类

    /**
     * @author ***
     * @title: SingletonDemo4
     * @projectName design-pattern
     * @description 单例模式:枚举类
     * @date 2021/7/5 16:45
     */
    public enum SingletonDemo5 {
        //直接用SingletonDemo06.INSTANCE本身代理,代表一个对象
        //避免反射、反序列化,效率高
        //没有懒加载
        INSTANCE;
        Enum ss;
    }

    6. 枚举类实现懒加载

    /**
     * @author ***
     * @title: SingletonDemo6
     * @projectName design-pattern
     * @description  单例模式:枚举类实现懒加载
     * @date 2021/7/5 16:49
     */
    public class SingletonDemo6 {
        // 私有构造函数
        private SingletonDemo6() {
    
        }
    
        public static SingletonDemo6 getInstance() {
            return Singleton.INSTANCE.getInstance();
        }
    
        private enum Singleton {
            INSTANCE;
    
            private SingletonDemo6 singleton;
    
            // JVM保证这个方法绝对只调用一次
            Singleton() {
                singleton = new SingletonDemo6();
            }
    
            public SingletonDemo6 getInstance() {
                return singleton;
            }
        }
    }

    7. 解决反射漏铜和序列化漏铜

    /**
     * @author ***
     * @title: SingletonDemo7
     * @projectName design-pattern
     * @description 单例模式:解决反射漏铜,通过设置constructor.setAccessible(true);//跳过权限校验,是的创建实例不一致
     *              解决序列化漏铜,readResolve()
     * @date 2021/7/5 16:51
     */
    public class SingletonDemo7 {
    
        //类初始化延迟加载,方法同步线程安全,调用效率低。
        private static SingletonDemo7 instance = null;
    
        private SingletonDemo7() {
            if (instance != null) {//防止通过反射破解单例
                throw new RuntimeException();
            }
        }
    
        public static synchronized SingletonDemo7 getInstance() {
    
            if (instance == null) {
                instance = new SingletonDemo7();
            }
            return instance;
        }
    
        //反序列化时,直接返回此对象,而不生成新对象
        private Object readResolve() throws ObjectStreamException {
            return instance;
        }
    }
  • 相关阅读:
    SharePoint 2010 User Profile Sync Service自动停止
    如何区别多个svchost.exe?
    Log Parser分析IIS log的一个简单例子
    Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
    Windows中右键点击文件夹, 结果找不到共享选项卡, 怎么办?
    介绍SOS中的SaveModule命令
    SharePoint中Draft版本的文档不会收到document added的Alert Email
    和我一起学Windows Workflow Foundation(1)创建和调试一个WF实例
    门户网站
    C#基础—— check、lock、using语句归纳
  • 原文地址:https://www.cnblogs.com/mylqm/p/14972897.html
Copyright © 2011-2022 走看看