JAVA本身有map接口及各种实现,Netty又根据自身的需要,进行了重新实现。个人感觉更多是针对使用习惯上及特定场景上的不同,进行重新的封装实现。(当然更多引用该接口的地方还没来的及看,有更多设计上的考虑也是有可能的)。
主要接口是AttributeKey和AttributeKey,对应map的key和value,AttributeMap对应的是简化的map接口。
public interface AttributeMap {
/**
* Get the {@link Attribute} for the given {@link AttributeKey}. This method will never return null, but may return
* an {@link Attribute} which does not have a value set yet.
*/
<T> Attribute<T> attr(AttributeKey<T> key);
/**
* Returns {@code true} if and only if the given {@link Attribute} exists in this {@link AttributeMap}.
*/
<T> boolean hasAttr(AttributeKey<T> key);
}
map接口只有两个函数,一个是判断是否存在,一个是返回对应的属性,但是没有put\remove等元素操作,那就只能放到Attribute里了。
public interface Attribute<T> {
AttributeKey<T> key();
T get();
void set(T value);
T getAndSet(T value);
T setIfAbsent(T value);
@Deprecated
T getAndRemove();
boolean compareAndSet(T oldValue, T newValue);
@Deprecated
void remove();
}
接口函数名很清晰表达了功能,其中key()可以返回key,整个接口看起来像是个加强版的Entry接口。而AttributeKey实现的是Constant接口。
/**
* A singleton which is safe to compare via the {@code ==} operator. Created and managed by {@link ConstantPool}.
*/
public interface Constant<T extends Constant<T>> extends Comparable<T> {
int id();
String name();
}
这个接口定义,我觉得重点在上面的注释做了说明,可以用==来判断是否相等,下面可以继续看实现。
看了接口之后,再看下一个使用场景:
ctx.channel().attr(ATTR).set(writer);
如果这里channel()实现的是java的map接口,是不是会写的比较复杂?没法直接返回entry就没法直接set。而这里的使用看起来确实更流畅一些。
public final class AttributeKey<T> extends AbstractConstant<AttributeKey<T>> {
private static final ConstantPool<AttributeKey<Object>> pool = new ConstantPool<AttributeKey<Object>>() {
@Override
protected AttributeKey<Object> newConstant(int id, String name) {
return new AttributeKey<Object>(id, name);
}
};
public static <T> AttributeKey<T> valueOf(String name) {
return (AttributeKey<T>) pool.valueOf(name);
}
public static boolean exists(String name) {
return pool.exists(name);
}
public static <T> AttributeKey<T> newInstance(String name) {
return (AttributeKey<T>) pool.newInstance(name);
}
public static <T> AttributeKey<T> valueOf(Class<?> firstNameComponent, String secondNameComponent) {
return (AttributeKey<T>) pool.valueOf(firstNameComponent, secondNameComponent);
}
private AttributeKey(int id, String name) {
super(id, name);
}
}
私有构造函数,用newInstance返回实例对象,加上使用ConstantPool来保存key值,,这个才是上面提到的能够用==来做判断的关键。
public abstract class ConstantPool<T extends Constant<T>> {
private final ConcurrentMap<String, T> constants = PlatformDependent.newConcurrentHashMap();
private final AtomicInteger nextId = new AtomicInteger(1);
public T newInstance(String name) {
return createOrThrow(checkNonEmpty(name, "name"));
}
private T createOrThrow(String name) {
T constant = constants.get(name);
if (constant == null) {
final T tempConstant = newConstant(nextId(), name);
constant = constants.putIfAbsent(name, tempConstant);
if (constant == null) {
return tempConstant;
}
}
throw new IllegalArgumentException(String.format("'%s' is already in use", name));
}
protected abstract T newConstant(int id, String name);
@Deprecated
public final int nextId() {
return nextId.getAndIncrement();
}
...
}
public abstract class AbstractConstant<T extends AbstractConstant<T>> implements Constant<T> {
private static final AtomicLong uniqueIdGenerator = new AtomicLong();
private final int id;
private final String name;
private final long uniquifier;
protected AbstractConstant(int id, String name) {
this.id = id;
this.name = name;
this.uniquifier = uniqueIdGenerator.getAndIncrement();
}
...
@Override
public final int hashCode() {
return super.hashCode();
}
@Override
public final int compareTo(T o) {
if (this == o) {
return 0;
}
@SuppressWarnings("UnnecessaryLocalVariable")
AbstractConstant<T> other = o;
int returnCode;
returnCode = hashCode() - other.hashCode();
if (returnCode != 0) {
return returnCode;
}
if (uniquifier < other.uniquifier) {
return -1;
}
if (uniquifier > other.uniquifier) {
return 1;
}
throw new Error("failed to compare two different constants");
}
...
}
ConstantPool使用的是ConcurrentHashMap作为存储结构,存储AttributeKey,同时保证唯一性;这个里面有点小疑问,就是ConstantPool生成的id,以及AbstractConstant的uniquifier,功能上是否有重复?如果用于排序,id也行啊,新建一个uniquifier是否真的有必要?
DefaultAttributeMap简单描述一下就是,使用数组作为存储结构,写时复制,按序存储。所以,只能适合小数据量的存储,读多写少的场景。
private volatile DefaultAttribute[] attributes = EMPTY_ATTRIBUTES;
@SuppressWarnings("unchecked")
@Override
public <T> Attribute<T> attr(AttributeKey<T> key) {
ObjectUtil.checkNotNull(key, "key");
DefaultAttribute newAttribute = null;
for (;;) {
final DefaultAttribute[] attributes = this.attributes;
final int index = searchAttributeByKey(attributes, key);
final DefaultAttribute[] newAttributes;
if (index >= 0) {
final DefaultAttribute attribute = attributes[index];
assert attribute.key() == key;
if (!attribute.isRemoved()) {
return attribute;
}
// let's try replace the removed attribute with a new one
if (newAttribute == null) {
newAttribute = new DefaultAttribute<T>(this, key);
}
final int count = attributes.length;
newAttributes = Arrays.copyOf(attributes, count);
newAttributes[index] = newAttribute;
} else {
if (newAttribute == null) {
newAttribute = new DefaultAttribute<T>(this, key);
}
final int count = attributes.length;
newAttributes = new DefaultAttribute[count + 1];
orderedCopyOnInsert(attributes, count, newAttributes, newAttribute);
}
if (ATTRIBUTES_UPDATER.compareAndSet(this, attributes, newAttributes)) {
return newAttribute;
}
}
}
attr的逻辑比较简单,有key对应的attribute就返回,否则新建,新建的场景下,都会更新整个数组。
private static final class DefaultAttribute<T> extends AtomicReference<T> implements Attribute<T> {
private static final AtomicReferenceFieldUpdater<DefaultAttribute, DefaultAttributeMap> MAP_UPDATER =
AtomicReferenceFieldUpdater.newUpdater(DefaultAttribute.class,
DefaultAttributeMap.class, "attributeMap");
private static final long serialVersionUID = -2661411462200283011L;
private volatile DefaultAttributeMap attributeMap;
private final AttributeKey<T> key;
DefaultAttribute(DefaultAttributeMap attributeMap, AttributeKey<T> key) {
this.attributeMap = attributeMap;
this.key = key;
}
@Override
public AttributeKey<T> key() {
return key;
}
private boolean isRemoved() {
return attributeMap == null;
}
@Override
public T setIfAbsent(T value) {
while (!compareAndSet(null, value)) {
T old = get();
if (old != null) {
return old;
}
}
return null;
}
@Override
public T getAndRemove() {
final DefaultAttributeMap attributeMap = this.attributeMap;
final boolean removed = attributeMap != null && MAP_UPDATER.compareAndSet(this, attributeMap, null);
T oldValue = getAndSet(null);
if (removed) {
attributeMap.removeAttributeIfMatch(key, this);
}
return oldValue;
}
@Override
public void remove() {
final DefaultAttributeMap attributeMap = this.attributeMap;
final boolean removed = attributeMap != null && MAP_UPDATER.compareAndSet(this, attributeMap, null);
set(null);
if (removed) {
attributeMap.removeAttributeIfMatch(key, this);
}
}
}
可见DefaultAttribute为了能直接进行remove操作,需要引用DefaultAttributeMap,是种比较巧妙的方式。同时不能用value直接构造Atrribute,必须先取得Atrribute,再用set的方式设置值。
整个看下来,技巧性的东西很值得学习。比如Attribute封装map的操作,用id来排序(用于二分查找),以及用map存储来使同名key唯一,以使用==来判断是否同一个key来代替equals判断。。。所有这些都是为了读多写少,key复用(每个channel都需要存储对应的key)的场景。类似的优化,Netty还有不少,性能的优化真的是犄角旮旯无处不在,功力都在细节处。