Java设计模式——单例模式

Java设计模式——单例模式

前言

  什么是单例模式?

   单例模式:⼀个类只允许产⽣⼀个实例化对象

  要想实现单例模式,毫无疑问的要做的就是限制对象的产生数量。而限制对象数量的步骤大体分为以下几点

   1、private声明构造方法
      断绝外部通过构造方法构造对象的可能
   2、单例类提供一个产生好的(全局)静态对象
      只能通过静态方法返回,外部不能产生对象,只能通过类来调用对象。
   3、单例类内部提供一个静态方法返回这个唯一的静态对象
      无论该方法被调用多少遍,始终操作的都是同一个全局对象

  由于要在内部产生一个实例化好的全局静态对象,因此,该对象具体在哪被实例化就是接下来所讨论的话题,众所周知,对象的实例化分为3种:

   定义时实例化
   静态代码块中实例化
   构造方法中实例化

  由于该对象是静态对象,因此在定义时实例化与静态代码块中实例化归为一种,都是在类加载的时候完成对象实例化。这种在类加载时完成对象实例化的方式成为"饿汉式单例"。
  这里举定义时实例化的例子,静态代码块的原理类似,明白这两种方式是在类加载时完成实例化即可。
	// 饿汉式单例模式
	class Singleton1 {
	
	    // 直接实例化(饿汉式)
	    private static final Singleton1 SINGLETON_1 = new Singleton1();
	
	    // 构造方法私有
	    private Singleton1() {
	    }
	
	    // 静态方法获得单例对象(外部连对象都不能创建,不能用普通方法)
	    public static Singleton1 getInstance() {
	        return SINGLETON_1;
	    }
	}
  "饿汉式单例"的优缺点:

   优点:写法比较简单,避免线程同步问题(类加载时实例化)
   缺点:没有达到Lazy Loading(延迟加载)的效果,若从始至终从未使用过这个实例,则会造成内存的浪费。

  由于此时构造方法时私有的,外部根本获取不到该构造方法,因此,需要在该类的getInstance()方法中使用构造方法实例化对象,由于单例模式只允许存在一个对象,则需要加上判断控制生成对象的个数。这种声明时不实例化而是放在方法中实例化,当第一次要使用该对象时才会实例化的方式称为"懒汉式单例",由于外部不能创建该类的实例化对象,因此该方法必须是静态的,外部可以通过类名.方法名进行访问。
	// 懒汉式单例(会有线程安全问题,不可用)
	class Singleton2{
	
	    // 只声明,不实例化(懒汉式)
	    private static Singleton2 singleton2 = null;
	
	    // 构造方法私有
	    private Singleton2(){}
	
	    // 静态方法获得单例对象(外部连对象都不能创建,不能用普通方法)
	    public static Singleton2 getInstance(){
	        if(singleton2 == null){
	            singleton2 = new Singleton2();
	        }
	        return singleton2;
	    }
	}
  "懒汉式单例"的优缺点:

   优点:达到了Lazy Loading的效果,在第一次需要使用该对象时才会创建。
   缺点:该方法具有线程安全问题,若多个线程同时进入getInstance()会导致产生多个对象,不符合单例模式的设计理念。

  “懒汉式单例"的改进:使用”双重检验锁模式+volatile禁止指令重排机制"

   双重加锁的意义:因为synchronized锁是加在if语句中,在多线程的情况下,多个线程都可以到达外层if语句中,如果不在锁中再次进行判断的话,会产生多个对象从而破坏单例模式(synchronized只是保证了同一时间只能有一个线程进入代码块,若多个线程都进入外层if语句,当一个线程创建对象后,另一个线程在下一个时间可以再创建对象,因此在同步代码块内还得再次进行判断)
  设置volatile变量的意义:singleton = new Singleton()其实是三步操作:
    ①在堆上分配空间
    ②属性初始化
    ③引用指向对象
  由于JVM存在指令重排序的隐患,②③的顺序是不能被保证的,如果一个对象已经 产生,但没有被初始化,就会报错,因此使用volatile禁止指令的重排序。从而达到,对象的创建一定会 先初始化属性再将引用指向对象

	class Singleton{
	
	    // volatile保证产生对象的完整性
	    private static volatile Singleton singleton;
	    
	    private Singleton(){}
	    
	    public static Singleton getInstance(){
	        if (singleton == null){     //singleton checked
	            synchronized (Singleton.class){
	                if (singleton == null){     //double checked
	                    singleton = new Singleton();
	                }
	            }
	        }
	        return singleton;
	    }
	}
  改进后的"懒汉式单例":线程安全,延迟加载,效率较高
  单例模式的其他实现方式可参考:单例模式的8种实现方式
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值