单例模式,绝对是面试时被问到最多的设计模式。如果搜索网上的java单例模式,这篇文章是说得最全面和深入的:
本文的脑图就是这篇文章的总结和精炼:百度脑图
脑图截图单例模式概念上是很简单的:提供和限制全局唯一的对象。复杂的是现实问题和实现方法。
衡量Java单例模式的好坏有几个方面:
- 多线程安全:多个线程调用单例方法不会产生多个对象才是安全的,才达到单例模式的目的。
- 延迟创建对象:单例模式创建的对象可能初始化一次需要耗费很多资源,内存、IO等,因此能不创建就不创建,只在调用单例方法的时候才创建。
- 简单易懂:有几种写法还是有些复杂和反直觉的,当然为了面试,可以背一背,研究一下背后的原理。比如volatile关键字,就值得研究一下。
- 防反射和序列化:从安全角度考虑,防止反射创建多个对象,防止反序列化时生成另一个对象。其实只有枚举法天生具有这两个能力,其他方法都可以用同样的方式达到这个目的。
另外,《游戏设计模式》一书中对单例模式的剖析很有趣,作者建议你不用单例模式。为什么?最直接的一个原因是:单例模式本质上就是一个全局变量啊!中文版传送门
以下是几种实现代码,脑图上放代码看起来有些费劲。
先定义一个获取方法的main方法来进行一个简单的测试,目的是判断是否延迟加载。
public class DemoSingleton {
public static void main(String[] args) {
SingletonClass.getInstance();
}
}
另一个测试,简单的测试一下是否是线程安全的,如果输出了两个构造函数的log那么肯定是不安全的。由于线程调度的不确定性,可能需要调整一下线程的数量,再多试几次。最好玩的是第三个方法中,去掉volatile关键字或者去掉一层判断,可以更好地理解为什么要写这么多代码来保证线程安全。
public class DemoSingleton {
public static void main(String[] args) {
for (int i = 0; i < 25; i++) {
new Thread(new Runnable(){
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
SingletonClass.getInstance();
}
}).start();
}
}
}
- 定义静态字段时直接初始化:简单;线程安全;不延迟
class SingletonClass {
private static SingletonClass instance = new SingletonClass();
public static SingletonClass getInstance() {
System.out.println("getInstance()");
return instance;
}
private SingletonClass() {
System.out.println("new SingletonClass");
}
}
// 输出
// new SingletonClass
// getInstance()
- 静态方法中简单判断唯一性:简单;线程不安全;延迟
class SingletonClass {
private static SingletonClass instance;
public static SingletonClass getInstance() {
System.out.println("getInstance()");
if (instance == null) {
instance = new SingletonClass();
}
return instance;
}
private SingletonClass() {
System.out.println("new SingletonClass");
}
}
// 输出
// getInstance()
// new SingletonClass
- 静态方法中判断唯一性考虑线程安全:复杂;线程安全;延迟
class SingletonClass {
private static volatile SingletonClass instance;
public static SingletonClass getInstance() {
System.out.println("getInstance()");
if (instance == null) {
synchronized(SingletonClass.class) {
if (instance == null) {
instance = new SingletonClass();
}
}
}
return instance;
}
private SingletonClass() {
System.out.println("new SingletonClass");
}
}
// 输出
// getInstance()
// new SingletonClass
- 静态内部类:简单;线程安全;延迟
class SingletonClass {
private static class SingletonHolder {
private static volatile SingletonClass instance = new SingletonClass();
}
public static SingletonClass getInstance() {
System.out.println("getInstance()");
return SingletonHolder.instance;
}
private SingletonClass() {
System.out.println("new SingletonClass");
}
}
// 输出
// getInstance()
// new SingletonClass
- 枚举法:诡异;线程安全;不延迟
enum SingletonClass {
INSTANCE;
public static SingletonClass getInstance() {
System.out.println("getInstance()");
return INSTANCE;
}
private SingletonClass() {
System.out.println("new SingletonClass");
}
}
// 输出
// new SingletonClass
// getInstance()
拜拜,煮面时顺溜
参考资料
http://www.tekbroaden.com/singleton-java.html
https://gpp.tkchu.me/singleton.html
网友评论