Java Map并发-Hashtable

全路径名:java.util.Hashtable

类的定义如下:

js 复制代码
/**
...
* @since JDK1.0
*/
public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {
    ...  
}

Hashtable 类实现了Map接口,JDK1.0 引入。

看下 Map 接口常用方法 put()、get() 的实现:

js 复制代码
public synchronized V put(K key, V value) {
    if (value == null) {
            throw new NullPointerException();
    }

    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                    V old = entry.value;
                    entry.value = value;
                    return old;
            }
    }

    addEntry(hash, key, value, index);
    return null;
}
js 复制代码
public synchronized V get(Object key) {
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                    return (V)e.value;
            }
    }
    return null;
}

不用去关心方法内部实现细节,从 synchronized 可以看出,使用了同步代码块机制,每次只能有一个线程进行操作。其他方法可以自己查看源码,都是采用 synchronized 方式实现的。

还需要关心的是 iterator() 方法。迭代时会不会抛出 ConcurrentModificationException 异常。看一下它的实现方式:

js 复制代码
...
private transient volatile Set<Map.Entry<K,V>> entrySet;
...
public Set<Map.Entry<K,V>> entrySet() {
    if (entrySet==null)
        entrySet = Collections.synchronizedSet(new EntrySet(), this);
    return entrySet;
}

entrySet() 方法调用了 ollections.synchronizedSet(); 下面是具体源码

js 复制代码
public class Collections {
   ...
   static <T> Set<T> synchronizedSet(Set<T> s, Object mutex) {
        return new SynchronizedSet<>(s, mutex);
    }

    /**
     * @serial include
     */
    static class SynchronizedSet<E>
          extends SynchronizedCollection<E>
          implements Set<E> {
        private static final long serialVersionUID = 487447009682186044L;

        SynchronizedSet(Set<E> s) {
            super(s);
        }
        SynchronizedSet(Set<E> s, Object mutex) {
            super(s, mutex);
        }

        public boolean equals(Object o) {
            if (this == o)
                return true;
            synchronized (mutex) {return c.equals(o);}
        }
        public int hashCode() {
            synchronized (mutex) {return c.hashCode();}
        }
    }
    ...
}

SynchronizedSet 类里没有 iterator() 方法。再看下父类 SynchronizedCollection

js 复制代码
static class SynchronizedCollection<E> implements Collection<E>, Serializable {

	final Collection<E> c;  // Backing Collection
	final Object mutex;     // Object on which to synchronize

	SynchronizedCollection(Collection<E> c) {
		this.c = Objects.requireNonNull(c);
		mutex = this;
	}
	...
	public Iterator<E> iterator() {
		return c.iterator(); // Must be manually synched by user!
	}

}

这里看到了最终是调用了 c.iterator() 方法。Collection c 是 Hashtable 的 entrySet() 方法调用时使用的:Collections.synchronizedSet(new EntrySet(), this) 。EntrySet 是Hashtable的一个内部类:

js 复制代码
private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
    public Iterator<Map.Entry<K,V>> iterator() {
            return getIterator(ENTRIES);
    }
    ...
}

使用了 ashtable 的 getIterator(int type) 方法:

js 复制代码
private <T> Iterator<T> getIterator(int type) {
    if (count == 0) {
        return Collections.emptyIterator();
    } else {
        return new Enumerator<>(type, true);
    }
}

最终使用的 Hashtable 的内部类 Enumerator,源码如下

js 复制代码
private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
    Entry<?,?>[] table = Hashtable.this.table;
    int index = table.length;
    Entry<?,?> entry;
    Entry<?,?> lastReturned;
    int type;
    boolean iterator;
    protected int expectedModCount = modCount;



    public boolean hasMoreElements() {
            Entry<?,?> e = entry;
            int i = index;
            Entry<?,?>[] t = table;
            /* Use locals for faster loop iteration */
            while (e == null && i > 0) {
                    e = t[--i];
            }
            entry = e;
            index = i;
            return e != null;
    }

    @SuppressWarnings("unchecked")
    public T nextElement() {
            Entry<?,?> et = entry;
            int i = index;
            Entry<?,?>[] t = table;
            /* Use locals for faster loop iteration */
            while (et == null && i > 0) {
                    et = t[--i];
            }
            entry = et;
            index = i;
            if (et != null) {
                    Entry<?,?> e = lastReturned = entry;
                    entry = e.next;
                    return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
            }
            throw new NoSuchElementException("Hashtable Enumerator");
    }

    // Iterator methods
    public boolean hasNext() {
            return hasMoreElements();
    }

    public T next() {
            if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            return nextElement();
    }

    public void remove() {
            if (!iterator)
                    throw new UnsupportedOperationException();
            if (lastReturned == null)
                    throw new IllegalStateException("Hashtable Enumerator");
            if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();

            synchronized(Hashtable.this) {
                    Entry<?,?>[] tab = Hashtable.this.table;
                    int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;

                    @SuppressWarnings("unchecked")
                    Entry<K,V> e = (Entry<K,V>)tab[index];
                    for(Entry<K,V> prev = null; e != null; prev = e, e = e.next) {
                            if (e == lastReturned) {
                                    modCount++;
                                    expectedModCount++;
                                    if (prev == null)
                                            tab[index] = e.next;
                                    else
                                            prev.next = e.next;
                                    count--;
                                    lastReturned = null;
                                    return;
                            }
                    }
                    throw new ConcurrentModificationException();
            }
    }
}

看 hasNext()、next()、remove(),以及它们最终调用的 hasMoreElements() 或者 nextElement() 可知,并发迭代会导致 ConcurrentModificationException。

相关推荐
未秃头的程序猿2 小时前
从零到一:深入浅出分布式锁原理与Spring Boot实战(Redis + ZooKeeper)
spring boot·分布式·后端
Soofjan2 小时前
MySQL(3.2):索引应用与优化
后端
黑牛儿2 小时前
PHP 8.3性能暴涨实测|对比8.2,接口响应提速30%,配置无需大幅修改
android·开发语言·后端·php
Soofjan2 小时前
MySQL(3.1):B+ 树索引原理
后端
Cache技术分享2 小时前
386. Java IO API - 监控目录变化
前端·后端
YanDDDeat2 小时前
【Spring】事务注解失效与传播机制
java·后端·spring
小陈工2 小时前
python Web开发从入门到精通(二十七)微服务架构设计原则深度解析:告别拆分烦恼,掌握治理精髓(上)
后端·python·架构
橙露2 小时前
MySQL 分库分表基础:水平拆分与路由规则实现
后端