单例模式
简介
单例模式是保证系统实例唯一性的重要手段。单例模式首先通过将类的实例化方法私有化防止程序通过其他方式创建该类的实例,然后通过提供一个全局唯一获取该类实例的方法帮助用户获取该类的实例,用户只需也只能通过调用该方法获取类的实例。
单例模式的常见写法有懒汉式,饿汉式,静态内部类,双重校验锁等。
具体实现
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.饿汉式
饿汉模式指在类中直接定义全局的静态对象的实例并初始化,然后提供一个方法获取该实例对象。懒汉式和饿汉式最大的不同在于,懒汉式在类中定义了单例但是未实例化,实例化的过程是在获取单例对象的方法中实现的,也就是说,第一次调用懒汉式时,该对象一定为空,然后去实例化对象并赋值,这样下次就能直接获取到对象了。饿汉式在定义单例对象的同时将其实例化了,直接使用即可。也就是说,在饿汉式下,在classloader 完成后该类的实例便已经存在于JVM中了。
//饿汉式 public class HungrySingleton { private static HungrySingleton instance = new HungrySingleton(); private HungrySingleton(){ } public static HungrySingleton getInstance(){ return instance; } }
3.静态内部类
通过在类中定义一个静态内部类,将对象实例的定义和初始化放在内部类中完成,我们在获取对象时要通过静态内部类调用其单例对象。之所以这样设计,是因为类的静态内部类在JVM中是唯一的,这样很好的保障了单例对象的唯一性。
同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟饿汉式不同的是:饿汉式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。
public class Singleton { private static class SingletonHolder{ private static final Singleton INSTANCE = new Singleton(); } private Singleton(){ } public static final Singleton getInstance(){ return SingletonHolder.INSTANCE; } }
4.双重校验锁
该模式是在懒汉模式的基础上做了进一步优化,给静态对象的定义加上volatile锁来保障初始化时对象的唯一性,在获取对象时通过synchronized(Singleton.class)给单例类加锁来保障操作的唯一性。
public class Lock2Singleton { private volatile static Lock2Singleton singleton; private Lock2Singleton(){ } public static Lock2Singleton getSingleton() { if (singleton == null) { synchronized (Lock2Singleton.class) { if (singleton == null) { singleton = new Lock2Singleton(); } } } return singleton; } }