本文来源:http://blog.csdn.net/zxhoo/article/details/17719333
感谢博主。本文仅供参考学习。
IoSession
MINA的IoSession接口定义了一组方法,让我们可以利用IoSession来存储一些数据:
- public interface IoSession {
- getAttribute(Object key)
- getAttribute(Object key, Object defaultValue)
- setAttribute(Object key)
- setAttribute(Object key, Object value)
- setAttributeIfAbsent(Object key)
- setAttributeIfAbsent(Object key, Object value)
- replaceAttribute(Object key, Object oldValue, Object newValue)
- removeAttribute(Object key)
- removeAttribute(Object key, Object value)
- containsAttribute(Object key)
- getAttributeKeys()
- }
AttributeMap接口
Netty将这种看似Map的功能进一步抽象,形成了AttributeMap接口:
- public interface AttributeMap {
- <T> Attribute<T> attr(AttributeKey<T> key);
- }
AttributeMap接口只有一个attr()方法,接收一个AttributeKey类型的key,返回一个Attribute类型的value。按照Javadoc,AttributeMap实现必须是线程安全的。AttributeMap内部结构看起来像下面这样:
谁实现了AttributeMap接口?
答案是所有的Channel和ChannelHandlerContext,如下面的类图所示:
AttributeKey
AttributeKey有两个地方值得 一提。第一是AttributeKey是个泛型类,在我看来,这也是Netty相对于MINA的一处改进。在使用IoSession的时候,你必须进行强制类型转换:
- int userId = (Integer) ioSession.getAttribute("userId");
但是使用AttributeMap却不需要:
- AttributeKey<Integer> KEY_USER_ID = AttributeKey.valueOf("userId");
- int userId = channel.attr(KEY_USER_ID).get();
第二是AttributeKey继承了UniqueName类,也就是说,对于每一个name,应该只有唯一一个AttributeKey与之对应。这一点看起来很奇怪,但是当知道DefaultAttributeMap内部使用了IdentityHashMap的时候,就觉得合情合理。下面是与AttributeKey相关的类图,至于UniqueName如何保证name唯一,稍后介绍:
UniqueName
UniqueName实际上是靠传入构造函数的一个map来保证name的唯一性:
- @Deprecated
- public class UniqueName implements Comparable<UniqueName> {
- private static final AtomicInteger nextId = new AtomicInteger();
- private final int id;
- private final String name;
- public UniqueName(ConcurrentMap<String, Boolean> map, String name, Object... args) {
- if (map == null) {
- throw new NullPointerException("map");
- }
- if (name == null) {
- throw new NullPointerException("name");
- }
- if (args != null && args.length > 0) {
- validateArgs(args);
- }
- if (map.putIfAbsent(name, Boolean.TRUE) != null) {
- throw new IllegalArgumentException(String.format("'%s' is already in use", name));
- }
- id = nextId.incrementAndGet();
- this.name = name;
- }
- ...
- }
但是Javadoc说这个类存在跟类加载器相关的问题,所以被废弃了。AttributeKey继承了UniqueName,内部使用ConcurrentHashMap来保证name的唯一性:
- public final class AttributeKey<T> extends UniqueName {
- private static final ConcurrentMap<String, Boolean> names = PlatformDependent.newConcurrentHashMap();
- @SuppressWarnings("deprecation")
- public static <T> AttributeKey<T> valueOf(String name) {
- return new AttributeKey<T>(name);
- }
- @Deprecated
- public AttributeKey(String name) {
- super(names, name);
- }
- }
Attribute接口
Attribute接口除了有必须的get()和set()和remove()方法外,还有几个原子方法:
- public interface Attribute<T> {
- AttributeKey<T> key();
- T get();
- void set(T value);
- T getAndSet(T value);
- T setIfAbsent(T value);
- T getAndRemove();
- boolean compareAndSet(T oldValue, T newValue);
- void remove();
- }
DefaultAttributeMap
如前面的类图所示,DefaultAttributeMap实现了AttributeMap接口,AbstractChannel和DefaultChannelHandlerContext通过继承DefaultAttributeMap也实现了AttributeMap接口。下面是DefaultAttributeMap的部分代码:
- public class DefaultAttributeMap implements AttributeMap {
- @SuppressWarnings("rawtypes")
- private static final AtomicReferenceFieldUpdater<DefaultAttributeMap, Map> updater =
- AtomicReferenceFieldUpdater.newUpdater(DefaultAttributeMap.class, Map.class, "map");
- // Initialize lazily to reduce memory consumption; updated by AtomicReferenceFieldUpdater above.
- @SuppressWarnings("UnusedDeclaration")
- private volatile Map<AttributeKey<?>, Attribute<?>> map;
- @Override
- public <T> Attribute<T> attr(AttributeKey<T> key) {
- Map<AttributeKey<?>, Attribute<?>> map = this.map;
- if (map == null) {
- // Not using ConcurrentHashMap due to high memory consumption.
- map = new IdentityHashMap<AttributeKey<?>, Attribute<?>>(2);
- if (!updater.compareAndSet(this, null, map)) {
- map = this.map;
- }
- }
- synchronized (map) {
- @SuppressWarnings("unchecked")
- Attribute<T> attr = (Attribute<T>) map.get(key);
- if (attr == null) {
- attr = new DefaultAttribute<T>(map, key);
- map.put(key, attr);
- }
- return attr;
- }
- }
- ...
- }
可以看到:
- map是延迟创建的(为了减少内存消耗),更准确的说,map在attr()方法第一次被调用的时候创建
- map被声明为volatile,再加上AtomicReferenceFieldUpdater.compareAndSet()方法的使用,map的null判断和赋值这段代码可以不使用synchronized
- 内部使用的是IdentityHashMap,所以AttributeKey必须是唯一的,因为IdentityHashMap使用==而不是equals()方法来判断两个key是否相同
- attr()方法被调用时,如果key还没有关联attribute,会自动创建一个
DefaultAttribute
最后,DefaultAttribute通过继承AtomicReference获得了原子操作能力:
- public class DefaultAttributeMap implements AttributeMap {
- private static final class DefaultAttribute<T> extends AtomicReference<T> implements Attribute<T> {
- private static final long serialVersionUID = -2661411462200283011L;
- private final Map<AttributeKey<?>, Attribute<?>> map;
- private final AttributeKey<T> key;
- DefaultAttribute(Map<AttributeKey<?>, Attribute<?>> map, AttributeKey<T> key) {
- this.map = map;
- this.key = key;
- }
- @Override
- public AttributeKey<T> key() {
- return key;
- }
- @Override
- public T setIfAbsent(T value) {
- while (!compareAndSet(null, value)) {
- T old = get();
- if (old != null) {
- return old;
- }
- }
- return null;
- }
- @Override
- public T getAndRemove() {
- T oldValue = getAndSet(null);
- remove0();
- return oldValue;
- }
- @Override
- public void remove() {
- set(null);
- remove0();
- }
- private void remove0() {
- synchronized (map) {
- map.remove(key);
- }
- }
- }
- }
下面是DefaultAttributeMap的内部结构:
结论
- Channel和ChannelHandlerContext都扩展了AttributeMap接口,因此每一个Channel和ChannelHandlerContext实例都可以像Map一样按照key来存取value
- AttributeMap实现必须是线程安全的,因此,attr()方法可以在任何线程里安全的调用
- AttributeKey必须是唯一的,因此最好定义成全局变量(比如static final类型)
- 默认的Attribute实现继承自AtomicReference,因此也是线程安全的