HashMap和可见性 [英] HashMap and visibility

查看:113
本文介绍了HashMap和可见性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

HashMap的javadoc 指出:

我构建了一个示例代码,基于规范,应该几乎立即失败并抛出ConcurrentModificationException; / p>


  • 它确实无法像Java 7那样立即失败

  • ,但它(似乎)总是与Java 6(即它不会抛出承诺的异常)。


    注意:它有时不会在Java 7中失败(比如1次20) - 我想它与线程调度有关(即2个runnables不交错)。



    我错过了什么吗?为什么使用Java 6运行的版本不会抛出ConcurrentModificationException?

    实际上,有2个Runnable任务并行运行(countdownlatch用于使他们开始大约在同一时间):


    • 一个是将项目添加到地图

    • 另一个是遍历地图,读取键并将它们放入数组中。



    然后主线程检查已经有多少个键添加到数组中。



    Java 7典型输出(迭代立即失败):


    java.util.ConcurrentModificationException

    MAX i = 0


    <强大的Java 6典型输出<整个迭代过程和数组包含所有添加的键>:

    blockquote>

    MAX i = 99


    使用的代码

      public class Test1 {

    public static void main(String [] args)throws InterruptedException {
    final int SIZE = 100;
    最终地图< Integer,Integer> map = new HashMap< Integer,Integer>();
    map.put(1,1);
    map.put(2,2);
    map.put(3,3);
    final int [] list = new int [SIZE];
    final CountDownLatch start = new CountDownLatch(1);
    Runnable put = new Runnable(){
    @Override
    public void run(){
    try {
    start.await();
    for(int i = 4; i< SIZE; i ++){
    map.put(i,i);
    }
    } catch(Exception ex){
    }
    }
    };

    Runnable iterate = new Runnable(){
    @Override
    public void run(){
    try {
    start.await();
    int i = 0; (Map.Entry< Integer,Integer> e:map.entrySet()){
    list [i ++] = e.getKey();
    Thread.sleep(1);
    }
    } catch(Exception ex){
    ex.printStackTrace();
    }
    }
    };
    ExecutorService e = Executors.newFixedThreadPool(2);
    e.submit(put);
    e.submit(iterate);
    e.shutdown();

    start.countDown();
    Thread.sleep(100);
    for(int i = 0; i< SIZE; i ++){
    if(list [i] == 0){
    System.out.println(MAX i =+一世);
    休息;
    }
    }
    }
    }

    :在x86机器上使用JDK 7u11和JDK 6u38(64位版本)。

    如果我们将看看 HashMap 来源并在Java 6和Java 7之间进行比较,我们将看到如此有趣的区别:

    瞬态Java6中的volatile int modCount; ,Java7中的 transient int modCount;

    我相信这是由于这种原因引起的代码的不同行为:

      if(modCount!= expectedModCount)
    抛出新的ConcurrentModificationException();

    UPD:在我看来,这是一个已知的Java 6 / 7错误: http://bugs.sun.com/bugdatabase/view_bug.do ?bug_id = 6625725 已在最新的Java7中修复。



    UPD-2: @Renjith先生说,他只是经过测试,并没有发现HashMaps实现的行为有任何不同。但我也只是测试过。



    我的测试是:
    $ b <1>我创建了 HashMap2 类,它是Java 6中 HashMap 的绝对副本。



    重要的是我们需要在这里介绍2个新字段:

    $ p $ transient volatile Set< K> keySet = null;

     瞬态易失性集合< V> values = null; 

    2)然后我使用 HashMap2 测试这个问题并在Java 7下运行它



    结果:它在 Java 6 ,即没有任何 ConcurentModificationException



    <这一切都证明了我的猜想。 Q.E.D。

    HashMap's javadoc states:

    if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException.

    I built a sample code that, based on the specification, is supposed to fail almost immediately and throw a ConcurrentModificationException;

    • It does fail immediately as expected with Java 7
    • but it (seems to) always work with Java 6 (i.e. it does not throw the promised exception).

    Note: it sometimes does not fail with Java 7 (say 1 time out of 20) - I guess it has to do with thread scheduling (i.e. the 2 runnables are not interleaved).

    Am I missing something? Why does the version run with Java 6 not throw a ConcurrentModificationException?

    In substance, there are 2 Runnable tasks running in parallel (a countdownlatch is used to make them start approximately at the same time):

    • one is adding items to the map
    • the other one is iterating over the map, reading the keys and putting them into an array

    The main thread then checks how many keys have been added to the array.

    Java 7 typical output (the iteration fails immediately):

    java.util.ConcurrentModificationException
    MAX i = 0

    Java 6 typical output (the whole iteration goes through and the array contains all the added keys):

    MAX i = 99

    Code used:

    public class Test1 {
    
        public static void main(String[] args) throws InterruptedException {
            final int SIZE = 100;
            final Map<Integer, Integer> map = new HashMap<Integer, Integer>();
            map.put(1, 1);
            map.put(2, 2);
            map.put(3, 3);
            final int[] list = new int[SIZE];
            final CountDownLatch start = new CountDownLatch(1);
            Runnable put = new Runnable() {
                @Override
                public void run() {
                    try {
                        start.await();
                        for (int i = 4; i < SIZE; i++) {
                            map.put(i, i);
                        }
                    } catch (Exception ex) {
                    }
                }
            };
    
            Runnable iterate = new Runnable() {
                @Override
                public void run() {
                    try {
                        start.await();
                        int i = 0;
                        for (Map.Entry<Integer, Integer> e : map.entrySet()) {
                            list[i++] = e.getKey();
                            Thread.sleep(1);
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            };
            ExecutorService e = Executors.newFixedThreadPool(2);
            e.submit(put);
            e.submit(iterate);
            e.shutdown();
    
            start.countDown();
            Thread.sleep(100);
            for (int i = 0; i < SIZE; i++) {
                if (list[i] == 0) {
                    System.out.println("MAX i = " + i);
                    break;
                }
            }
        }
    }
    

    Note: using JDK 7u11 and JDK 6u38 (64 bits version) on an x86 machine.

    解决方案

    If we will look into HashMap sources and compare them between Java 6 and Java 7 we will see such interesting difference:

    transient volatile int modCount; in Java6 and just transient int modCount; in Java7.

    I'm sure that it is cause for different behavior of mentioned code due to this:

            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
    

    UPD: It seems to me, that this is a known Java 6/7 bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6625725 which was fixed in latest Java7.

    UPD-2: Mr. @Renjith said, that he just tested and did not found any difference in behavior of HashMaps implementation. But I just tested it too.

    My test was:

    1) I have created HashMap2 class, which is absolutely copy of HashMap from Java 6.

    One important thing is we need to introduce here 2 new fields:

    transient volatile Set<K>        keySet = null;
    

    and

    transient volatile Collection<V> values = null;
    

    2) Then I used this HashMap2 in test of this question and run it under Java 7

    Result: it works like such test under Java 6, i.e. there isn't any ConcurentModificationException.

    That all proves my conjecture. Q.E.D.

    这篇关于HashMap和可见性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆