单例模式是一个老生常谈的话题了,如何实现呢?我们这里不再赘述,我们这里梳理一下思路,看看设计一个单例模式需要考虑的问题。
1,延迟加载
new操作放在方法内部,不要作为类的静态成员变量暴露给调用者。
2,只实例化一次
实例化之前先判断是否为null。
3,线程安全
有多种思路:synchronized关键字,内部类等。
synchronized关键字其实就是加锁,这是jvm给我们提供的一种线程安全机制。
内部类是使用到了jvm类加载机制,jvm能够保证一个类在加载的时候是线程安全的。
4,规避反射调用
反射机制增强了java的动态性,但是也会带来一些问题。比如我们这里要讨论的,反射机制破坏了单例模式,为什么呢?因为反射机制可以绕过java类的访问权限,调用java类的私有构造方法,生成类对象。
兵来将挡水来土掩。要解决这个问题,我们最常用的方式是拋异常。
好了,只要掌握了这几个要点,然后结合自己的业务场景,相信你也可以写出自己的单例类了。
下面我们看一个实例:Logger实例。
我们在实际开发中,打印日志都会这样来写:
private static final Logger logger = LoggerFactory.getLogger(Xxx.class);
Slf4j日志框架在生成logger实例时,使用了单例模式,当然,还有其他设计模式,比如工厂模式和门面模式等。
我们看一下源码,源码在LoggerFactory.class中,最终调用方法getLoggerFactory(),该方法源码如下:
public static ILoggerFactory getILoggerFactory(){
if (INITIALIZATION_STATE == 0){
Class var0 = LoggerFactory.class;
synchronized(LoggerFactory.class){
if (INITIALIZATION_STATE == 0){
INITIALIZATION_STATE == 1;
performInitialization();
}
}
}
}
performInitialization()方法最终会调用StaticLoggerBinder.getSingleton(),该方法源码如下:
// Logger实例没有延迟加载,而是初始化StaticLoggerBinder的时候就加载了
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();3
public static StaticLoggerBinder getSingleton(){
return SINGLETON;
}