【Netty】【源码】AttributeMap相关

简介

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。而这里的使用看起来确实更流畅一些。

AttributeKey

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及DefaultAttribute

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还有不少,性能的优化真的是犄角旮旯无处不在,功力都在细节处。

你可能感兴趣的:(java,netty)