单例模式(饿汉、懒汉)优化

单例模式介绍:

保证一个类只有一个实例,并提供一个全局访问点,单例模式为了防止外部主动创建内,故而把构造方法设为私有

 

单例模式构建思路:

对象是怎么来的?new=>调用构造方法。所以需要控制构造方法,不允许随便在外部创建对象

1、私有化构造方法

那外部怎么得到对象呢?通过自己提供的get方法return一个对象。同时get方法里不能创建对象,不然外部每次调用get方法都会创建对象,所以对象只能在get方法外面创建。get方法不能通过对象调用了,所以必须声明为static方法,通过类名.get方法调用。

2、创建一个静态的get方法

创建一个成员变量保存对象,由于不能让外部访问,所以应该用private修饰。又因为需要get方法访问,而get方法是static修饰的,只能访问static修饰的成员变量,所以该变量也要用static修饰。还可以加上一个final表示不可变性。

3、在类中声明一个private、static、final修饰的成员变量用于保存对象。

 

饿汉模式:

public class Singleton {
	/**
	 * 3. 创建对象
	 */
	private static final Singleton INSTANCE = new Singleton();
	/**
	 * 1. 私有化构造方法
	 */
	private Singleton(){}
	/**
	 * 饿汉模式
	 * 2. 在类的内部创建一个对象,并且将该对象,返回给外部,通过get方法
	 * 		但是非static方法,只能通过对象调用,而我们不能在外部得到一个对象,所以只能通过类.get方法的方式调用,所以该方法要声明为 static方法
	 * @return
	 */
	public static Singleton getInstance(){
		return INSTANCE;
	}
}

懒汉模式:

public class Singleton {
	private static Singleton INSTANCE;
	/**
	 * 1. 私有化构造方法
	 */
	private Singleton(){}
	/**
	 * 懒汉模式
	 * 2. 在类的内部创建一个对象,并且将该对象,返回给外部,通过get方法
	 * 		但是非static方法,只能通过对象调用,而我们不能在外部得到一个对象,所以只能通过类.get方法的方式调用,所以该方法要声明为 static方法
	 * @return
	 */
	public static Singleton getInstance(){
		//判断当前对象是否已经创建了
		if (INSTANCE == null) {
			INSTANCE =  new Singleton();
		}
		return INSTANCE;
	}
}

饿汉模式:天生就是线程安全的
      优势:
             因为在类一加载就创建了对象,所有当需要这个对象的时候,获取对象的效率高
      劣势:
             类加载的时候,效率低,会一直占用内存空间,只有类卸载,才能释放空间

懒汉模式: 当需要对象的时候才创建对象。
     优势:
            节省资源空间,线程不安全
     劣势:
            获取对象的时候,临时创建,所以效率低

饿汉的优化:(用静态代码块创建对象,比较:没优化前是在编译的时候(类加载的时候)就会创建对象,静态代码块是在该类被使用的时候才会被调用,变得和懒汉一样,在需要创建对象的时候才会被调用)

/**
 *	饿汉模式:
 *		优点:
 *			线程安全
 *			获取对象效率高
 *
 *		缺点:占用空间,类加载效率低
 *
 *	优化的是启动速度
 *	静态代码块在需要的时候加载内部的代码
 */
public class Singleton {
	private static final Singleton INSTANCE;
	//优化代码
	static{
		System.out.println("静态代码块");
		INSTANCE = new Singleton();
	}
	
	private Singleton(){}
	
	public static Singleton getInstance(){
		return INSTANCE;
	}
}

懒汉的优化:(双重校验锁)

/**
 *	懒汉模式:
 *		优点:
 *			节省空间,启动速度快
 *
 *		缺点:
 *			线程不安全
 *			获取对象效率低
 *
 *	优化:双重校验锁,优化的时线程安全问题
 */
public class Singleton {
	private volatile static Singleton INSTANCE;
	
	private Singleton(){
		
	}
	
	public static Singleton getInstance() {
		//判断是否已经被创建对象,如果创建了,则直接返回创建好的对象
		if (INSTANCE == null) {
			//有线程安全的代码,同步
			synchronized (Singleton.class) {
				//避免线程安全问题,再次判断是否被创建了对象
				if (INSTANCE == null) {
					INSTANCE = new Singleton();
				}
			}
		}
		return INSTANCE;
	}
}

分析:

分析

更高效的懒汉模式

public class Singleton {
    /**
     * 类级的内部类,也就是静态的成员式内部类,和其他被static修饰的成员类似
     * 属于类级别的,和对象无关,只有在被调用的时候才会被装载,由于类被装载
     * 时属于线程安全的,即由JVM内部为我们保证线程安全
     */
    private  static class SingletonHolder{
        /**
         * 静态初始化器,有JVM来保证线程安全
         */
        private static Singleton instance = new Singleton();
    }

    /**
     * 私有化构造方法
     */
    private Singleton(){ 
        if (SingletonHolder.instance != null) {
            throw new IllegalStateException();
        }
    }
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

相比于懒汉以及饿汉模式,静态内部类模式(一般也被称为 Holder)是许多人推荐的一种单例的实现方式,因为相比懒汉模式,它用更少的代码量达到了延迟加载的目的。

顾名思义,这种模式使用了一个私有的静态内部类,来存储外部类的单例,这种静态内部类,一般称为 Holder。

而利用静态内部类的特性,外部类的 getinstance() 方法,可以直接指向 Holder 持有的对象。

 

  • 反射能否打破单例?

  首先,对外部类的私有构造器中加入 instance==null 的判断,防止反射入侵外部类。

  其次,静态内部类保证了从外部很难获取 SingletonHolder 的 Class 对象,从而保证了内部类不会被反射。

 

  • 多线程能否打破单例?

  Holder 模式借用了饿汉模式的优势,就是在加载类(内部类)的同时对 instance 对象进行初始化。

  由于自始至终类只会加载一次,所以即使在多线程的情况下,也能够保持单例的性质。

 

  • 优势?劣势?

  优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。

  劣势:需要多加载一个类;相比于懒汉模式,Holder 创建的单例,只能通过 JVM 去控制器生命周期,不能手动 destroy。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值