单例Singleton
确保一个类只有一个实例,并提供该实例的全局访问点。Singleton通常被用来代表那些本质上唯一的系统组件,比如窗口管理器或者文件系统。
使用一个私有构造函数,一个私有静态变量以及一个公有静态函数来实现。私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。
最简单的单例实现:饿汉模式
1 | public class Singleton { |
这种实现方式虽然不是最好的实现方式,但是是最常用的单例的实现方式。因为类一开始即被装载,所以不用担心线程安全的问题。但是缺点就是如果不使用这个类,就会存在内存浪费的问题。
线程安全:双重检查模式
加锁操作只需要对实例化部分的代码进行,只有当instance没有被实例化时,才需要进行加锁。
1 | public class Singleton { |
为什么在锁的内部还有再加一层if判断呢,如果只有一个if语句,在instance==null的情况下,如果两个线程都进入了if语句块中,虽然在if语句块中有加锁操作,但两个线程都会执行实例化instance= new Singleton()
这条语句,只是时间问题。那么就会进行两次实例化。破坏了单例模式。因此需要两个if语句:第一个语句用来避免instance已经被实例化后的加锁操作,第二个if语句进行了加锁,只有一个线程进入,不会出现多次实例化的情况。
静态内部类实现
当Singleton类被加载时,静态内部类SingletonHolder没有被加载进内存。只有当调用getInstance()方法时从而触发SingletonHolder.instance时SingletonHolder才会被加载。此时初始化INSTANCE实例,并且JVM确保INSTANCE只能被实例化一次。
这种方式不仅具有延迟初始化的好处,而且由JVM提供了对线程安全的支持。
1 | public class Singleton { |
JVM装载内部类并不是程序启动就装载,而且装载内部类是线程安全的。所以这个单例模式真正意义上实现了懒加载与线程安全且节省了内存。
枚举实现
实现单例模式只需编写一个包含单个元素的枚举类型:
1 | public enum Singleton { |
简洁,且无偿地提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。可以防止反射攻击,防止反序列化重新创建新的对象。
参考资料
[1] Effective Java 中文版
[2] 技术面试必备基础知识
[3] CodeSheep单例模式