从HDFS中学习单例模式
在平时工作的时候,各种优秀的开源组件中的代码风格、架构设计、奇技淫巧等是值得我们深入学习的。这样当我们要从0开始设计一个大的系统时,就可以参照这些组件的实践经验来快速开发。但在这之前我们有针对性地对这些良好的实践进行积累。
举个例子,假设我们要开发一个系统,它需要有监控模块,这块就可以参考Hadoop、Alluxio等开源组件,看它们的监控模块是怎么实现的。当然不同的组件监控模块的实现不一样,需要根据实际情况进行取舍。
那本文我们就来通过HDFS学习使用静态内部类实现单例模式的实践:
一、经典的实现
代码如下:
package com.zhb;
public class Singleton {
/** 私有化构造器 */
private Singleton() {
}
/** 对外提供public的获取实例的方法 */
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/** 写一个静态内部类,里面实例化外部类 */
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
有几个要点:
① 这种情况是懒加载的。只有调用到getInstance方法的时候,才会new Singleton。为什么?因为JVM规定了类初始化的时机,没有规定类初始化时同时初始化内部类。访问类的静态成员的时候才会去初始化类。
②是单例的,为什么?因为INSTANCE是个静态常量,只会被初始化一次。
③线程安全的,为什么?类加载的过程是线程安全的,这是JVM做的保证。JVM内部会保证一个类的
二、HDFS中用静态内部类实现单例设计模式
为方便理解,大家可以忽略掉一些HDFS中的专有名词,可以仅仅把它当成一个类。
在DFSClient类中getLeaseRenewer()方法用于获取“lease renewer”的实例。
/** Return the lease renewer instance. The renewer thread won't start
* until the first output stream is created. The same instance will
* be returned until all output streams are closed.
*/
public LeaseRenewer getLeaseRenewer() {
return LeaseRenewer.getInstance(
namenodeUri != null ? namenodeUri.getAuthority() : "null", ugi, this);
}
LeaseRenewer的构造方法是private的,所以外部无法直接new LeaseRenewer对象。只能通过LeaseRenewer.getInstance方法获得LeaseRenewer对象。
继续看getInstance()方法。
/** Get a {@link LeaseRenewer} instance */
public static LeaseRenewer getInstance(final String authority,
final UserGroupInformation ugi, final DFSClient dfsc) {
// Factory是LeaseRenewer类的静态内部类。
final LeaseRenewer r = Factory.INSTANCE.get(authority, ugi);
r.addClient(dfsc);
return r;
}
重点在Factory.INSTANCE.get()方法,这里与经典的单例不同,做了些小修改,不过原理是一样的。Factory.INSTANCE也是单例的,只不过这个INSTANCE是Fatory对象,之后可以用它调用静态内部类Factory里面的方法进行一些逻辑的处理。因为INSTANCE是单例的,所以外部或得到的都是同一个Factory对象,调用Factory对象的get方法也就是同一个get方法。这里get方法又通过synchronized修饰,同一时刻只有一个线程可以执行。在get方法里根据参数从renewers这个Map中获取对应的LeaseRenewer对象。
最后把代码整合一下,方便理解:
public static LeaseRenewer getInstance(final String authority,
final UserGroupInformation ugi, final DFSClient dfsc) {
// Factory是LeaseRenewer类的静态内部类。
final LeaseRenewer r = Factory.INSTANCE.get(authority, ugi);
r.addClient(dfsc);
return r;
}
public class LeaseRenewer {
/**
* A factory for sharing {@link LeaseRenewer} objects
* among {@link DFSClient} instances
* so that there is only one renewer per authority per user.
*/
private static class Factory {
private static final Factory INSTANCE = new Factory();
/** Get a renewer. */
private synchronized LeaseRenewer get(final String authority,
final UserGroupInformation ugi) {
final Key k = new Key(authority, ugi);
LeaseRenewer r = renewers.get(k);
if (r == null) {
r = new LeaseRenewer(k); // 静态内部类new外部类。
renewers.put(k, r);
}
return r;
}
//省略其他。。。
}
}