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;
}
}