• Java设计模式-单例模式


    Java设计模式-单例模式

    一、概述

    单例模式的定义就是确保某一个类只有一个实例,并且提供一个全局访问点。属于设计模式三大类中的创建型模式
    单例模式具有典型的三个特点

    • 只有一个实例。
    • 自我实例化。
    • 提供全局访问点。

    二、优缺点

    优点:

    1、由于单例模式只生成了一个实例,所以能够节约系统资源,减少性能开销,提高系统效率,同时也能够严格控制客户对它的访问。
    2、实例控制:Singleton 会阻止其他对象实例化其自己的 Singleton 对象的副本,从而确保所有对象都访问唯一实例
    3、灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程

    缺点:

    1、开销:虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题,上面的五种实现方式中已经说过了。
    ​2、可能的开发混淆:使用 singleton 对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用 new 关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
    3、对象的生存期:Singleton 不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于 .NET Framework 的语言),只有 Singleton 类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致 Singleton 类中出现悬浮引用。
    4、扩展困难:也正是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,这样扩展起来有一定的困难。

    三、常见实现方式

    常见的单例模式实现方式有五种:饿汉式懒汉式双重检测锁式静态内部类式枚举单例。而在这五种方式中饿汉式懒汉式又最为常见。下面将一一列举这五种方式的实现方法:

    • 饿汉式线程安全,调用效率高。但是不能延时加载。示例:
    public class SingletonDemo1 {
        // 线程安全的
        // 类初始化时,立即加载这个对象
        private static SingletonDemo1 instance = new SingletonDemo1();
        private SingletonDemo1() {
        }
        // 方法没有加同步块,所以它效率高
        public static SingletonDemo1 getInstance() {
            return instance;
        }
    }
    

    由于该模式在加载类的时候对象就已经创建了,所以加载类的速度比较慢,但是获取对象的速度比较快,且是线程安全的。

    • 懒汉式线程不安全
     // 线程不安全的
    public class SingletonDemo2 {
        private static SingletonDemo2 instance = null;
        private SingletonDemo2() {}
        // 运行时加载对象
        public static SingletonDemo2 getInstance() {
            if (instance == null) {
                instance = new SingletonDemo2();
            }
            return instance;
        }
    }
    

    由于该模式是在运行时加载对象的,所以加载类比较快,但是对象的获取速度相对较慢,且线程不安全如果想要线程安全的话可以加上synchronized关键字,但是这样会付出惨重的效率代价。

    • 懒汉式(双重同步锁)
    public class Singleton(){
    	private volatile static Singleton signleton;
    	private Singleton(){}
    	public static Singleton getSingleton(){
    		if(singleton == null){
    			synchronized (Singleton.class){
    				if(singleton == null){
    					singleton = new Singleton();
    				}
    			}
    		}
    		return signleton;
    	}
    }
    
    • 静态内部类单例
    public class Singleton(){
    	private static class SingletonHolder (){
    		private static final Singleton INSTANCE = new Singleton();
      }
      private Singleton(){}
      public static final Singleton getINstance(){
        	return SingletonHolder.INSTANCE;
      }
    }
    

    ​ 这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。

    • 枚举式
    // 方法
    public enum Singleton(){
    	INSTANCE;
    	public void whateverMethod() {}
    }
    

    ​ 这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。

    :注意单例模式所属类的构造方法是私有的,所以单例类是不能被继承的

    四、常见应用场景

    • 网站计数器。
    • 项目中用于读取配置文件的类。
    • 数据库连接池。因为数据库连接池是一种数据库资源。
    • Spring中,每个Bean默认都是单例的,这样便于Spring容器进行管理。
    • Servlet中Application
    • Windows中任务管理器,回收站。
      等等。
  • 相关阅读:
    12.静态目录
    11.Git线上操作
    10.分离的前后台交互
    09.后台主页应用
    08.前端主页
    爬虫介绍
    python2与python3爬虫中get与post对比
    HTML+CSS+JavaScript
    数据库 Mysql-mongodb-redis
    时频工具箱介绍与使用
  • 原文地址:https://www.cnblogs.com/itcod/p/14927168.html
Copyright © 2020-2023  润新知