项目中需要将一些对象存放到Hashtable中,当满足一定条件,再移除。同时还会有一个定时器定时检查Hashtable中的值,如果长时间无操作,也会移除Hashtable。
下面是代码模拟:
- Java code
public class TT { public static void main(String[] args) { //模拟3200次 for(int y=0;y<3200;y++){ Hashtable<Integer, String> hts = new Hashtable<Integer, String>(); for(int i=0;i<300;i++){ hts.put(i, "value="+i); } new Thread(new TestThread2(hts)).start(); new Thread(new TestThread(hts)).start(); } }}
#TestThread
- Java code
public class TestThread implements Runnable { private Hashtable<Integer, String> hts; public TestThread(Hashtable<Integer, String> hts) { this.hts = hts; } @Override public void run() { //移除 hts.remove(1); }}
#TestThread2
- Java code
public class TestThread2 implements Runnable { private Hashtable<Integer, String> hts; public TestThread2(Hashtable<Integer, String> hts) { this.hts = hts; } @Override public void run() { // 遍历 //方法1 Iterator<Integer> it = hts.keySet().iterator(); while(it.hasNext()){ int k= it.next(); //此处抛 ConcurrentModificationException System.out.println(k); } //方法2// Enumeration<Integer> e = hts.keys();// while (e.hasMoreElements()) {// int key = e.nextElement(); //多次测试此处不会抛异常// System.out.println(key+"--"+hts.get(key));// } }}
1、遍历的时候使用方法2为何不会抛异常?查看JDK文档说Iterator接口不是替换Enumeration的吗?
2、Hashtable不是线程安全的吗,为何方法1还会有ConcurrentModificationException异常??
还请各位高手解答……
------解决方案--------------------
ConcurrentModificationException是因为你用迭代器的同时 还在外部用了remove 这是不允许的 你的remove操作也要在迭代器里完成
尝试用一下CopyOnWriteArrayList 不过它的Iterator迭代出的值只是Iterator创建的 和后来修改无关.........
------解决方案--------------------
Iterator的next方法里面会调用 checkForComodification()判断集合是否被修改过,如果修改过就抛出ConcurrentModificationException异常。所以你的程序虽然是在其他地方remove,却是在next处抛异常。这个和是否是线程安全的没关系,是迭代器不允许。
Enumeration的nextElement方法没有进行“集合是否被修改”的检测,所以没有抛出异常。
------解决方案--------------------
Hashtable 中的迭代器不是线程安全的
建议使用 ConcurrentHashMap 这个是真正意义上的线程安全,包括迭代器
另外,如果你想实现 LRU 算法的 Map 的话,推荐使用继承 LinkedHashMap 重写 removeEldestEntry 方法就可以了。