1、单例模式简介
单例模式是保证系统实例唯一性的重要手段。单例模式首先通过将类的实例化方法私有化来防止程序通过其他方式创建该类的实例,然后通过提供一个全局唯一获取该类实例的方法帮助用户获取类的实例,用户只需要也只能通过调用该方法获取类的实例。
单例模式的设计保证了一个类在整个系统中同一时刻只有一个实例存在,主要被用于一个全局类的对象在多个地方被使用并且对象的状态是全局变化的场景下。同时,单例模式为系统资源的优化提供了很好的思路,频繁创建和销毁对象都会增加系统的资源消耗,而单例模式保障了整个系统只有一个对象能被使用,很好地节约了资源。
单例模式的实现很简单,每次在获取对象前都先判断系统是否已经有这个单例对象,有则返回,没有则创建。需要注意的是,单例模式的类构造函数是私有的,只能由自身创建和销毁对象,不允许除该类外的其他程序使用new关键字创建对象。
单例模式的常见模式有懒汉模式(线程安全)、饿汉模式、静态内部类、双重校验锁。
2、单例模式实现
2.1 懒汉模式(线程安全)
懒汉模式很简单:定一一个私有的静态对象instance
,之所以定义instance
为静态,是因为静态属性或方法是属于类的,能够很好地保障单例对象的唯一性;然后定义一个加锁的静态方法获取该对象,如果该对象为null
,则定一一个对象实例并将其赋值给instance
,这样下次再获取该对象时便能够直接获取了。
懒汉模式在获取对象实例时做了加锁操作,因此是线程安全的。
//懒汉模式(线程安全)
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton(){}
public static synchronized LazySingleton getInstance(){
if(instance==null){
instance=new LazySingleton();
}
return instance;
}
}
2.2 饿汉模式
饿汉模式指在类中直接定义全局的静态对象的实例并初始化,然后提供一个方法获取该实例对象。懒汉模式和饿汉模式的最大不同在于,懒汉模式在类中定义了单例但是并未实例化,实例化的过程是在获取单例都西昂的方法中实现的,也就是说,在第一次调用懒汉模式时,该对象一定为空,然后去实例化对象并赋值,这样下次就能直接获取对象了;而饿汉模式是在定义单例对象的同时将其实例化的,直接使用便可。也就是说,在饿汉模式下,在Class Loader
完成后该类的实例便已经存在于JVM
中了。
//饿汉式
public class HungrySingleton {
private static HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return instance;
}
}
2.3 静态内部类
静态内部类通过在类中定义一个静态内部类,将对象实例的定义和初始化放在内部类中完成,我们再获取对象时要通过静态内部类调用其单例对象。之所以这样设计,是因为类的静态内部类在JVM
中是唯一的,这很好地保障了单例对象的唯一性。
//静态内部类
public class Singleton {
private static class SingletonHolder{
private static final Singleton INSTANCE=new Singleton();
}
private Singleton(){}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
2.4 双重校验锁
双锁模式在懒汉模式的基础上作了进一步优化,给静态对象的定义加上volatile
来保障初始化时对象的唯一性,在获取对象时通过synchronized(Singleton.class)
给单例类加锁来保障操作的唯一性。
//双重校验锁
public class Lock2Singleton {
private volatile static Lock2Singleton singleton; //1:对象锁
private Lock2Singleton(){}
public static Lock2Singleton getSingleton(){
if(singleton==null){
synchronized (Lock2Singleton.class){ //2:synchronized方法锁
if(singleton==null){
singleton=new Lock2Singleton();
}
}
}
return singleton;
}
}
第一次
if(singleton==null)
:为了提高代码的执行效率,由于单例模式只要一次创建实例即可,所以当创建了一个实例之后,再次调用getInstance
方法就不同进入同步代码块,不用竞争锁。第二次
if(singleton==null)
:这个校验时防止二次创建实例。