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。

相关推荐
阿丰资源17 小时前
基于Spring Boot的网上摄影工作室系统(源码一键运行)
java·spring boot·后端
AI人工智能+电脑小能手17 小时前
【大白话说Java面试题】【Java基础篇】第40题:Java中的深拷贝和浅拷贝有什么区别
java·开发语言·后端·面试
小强198819 小时前
为什么你建了索引,查询还是很慢?常见失效原因汇总
后端
长大198819 小时前
MySQL 索引到底是什么?普通人也能看懂的通俗讲解
后端
阿苟19 小时前
spring重点详解
java·后端·面试
l软件定制开发工作室20 小时前
Spring开发系列教程(35)——使用Actuator
java·后端·spring
我叫黑大帅20 小时前
PyScript-GitHubRepo: 构建高性能GitHub仓库批量下载工具的技术实践
后端·python·面试
平凡但不平庸的码农21 小时前
Go 错误处理详解
开发语言·后端·golang
请你喝可乐1 天前
AI Agent Skill 高阶使用指南:从入门到精通
后端
用户962377954481 天前
代码审计 | Struts2 —— S2-016 OGNL 注入原理
后端