【问题标题】:Retries before lock and Locking scenarios in ConcurrentHashMapConcurrentHashMap 中的锁定和锁定场景之前的重试
【发布时间】:2013-09-05 11:15:18
【问题描述】:

来自ConcurrentHashMap的源码

/**
  171        * Number of unsynchronized retries in size and containsValue
  172        * methods before resorting to locking. This is used to avoid
  173        * unbounded retries if tables undergo continuous modification
  174        * which would make it impossible to obtain an accurate result.
  175        */
  176       static final int RETRIES_BEFORE_LOCK = 2;

1.我读过迭代不持有锁,那么上面的语句是什么意思? get之类的操作也可以锁吗?也请提供场景。

2.如果尚未对该元素进行迭代,线程 1 中运行的更新操作是否对线程 2 中的迭代可见?(波动性和可见性?)

3.除了更新之外,还有什么其他情况需要加锁吗?

4. 获取数据时,使用的是 volatile 读取。如果 volatile 读取导致未命中,则在最后一次尝试成功读取时获取该段的锁定。这是什么意思?什么是 volatile 读取?

【问题讨论】:

    标签: java multithreading


    【解决方案1】:

    我已经读过迭代不持有锁,那么上面的语句是什么 方法?

    可以争辩说 size 方法可以继续执行并且永远不会持有锁。但是这个实现会两次获取 ConcurrentHashMap 的大小,如果第一个的大小不等于第二个,它将重试。如果同样如此,它将锁定所有段并最后一次获取大小。

    get 之类的操作也可以持有锁吗?请提供情景 也。

    从技术上讲可以,但可能永远不会发生。如果 JVM 在条目可用后发布 CHM 的条目条目值之一,CHM 将在段锁定下进行读取(同样这可能永远不会发生)。

    Java 8 正在发布 CHM 的新实现,因此它可能很快就会过时。

    在线程 1 中运行的更新操作是否对迭代可见 如果尚未对该元素进行迭代,则在线程 2 中 制造?(波动性?)

    如果线程 1 在线程 2 发出 get 之前发出 put,那么线程 2 将看到更新的条目。

    如果线程 2 在执行 get 的同时线程 1 正在执行 put,那么线程 2 可能会或可能不会看到条目(或者它可能会,这是时间问题)。这是因为get 是非阻塞的。这仍然是线程安全的,因为 CHM 说它会在您搜索地图时返回条目。

    除了更新之外,还有什么其他情况需要加锁吗?

    除了所有的修改方法,序列化还需要一个锁。

    获取数据时,使用易失性读取。如果易失性读取 导致未命中,然后获得该段的锁定 最后一次尝试读取成功。这是什么意思?什么是 volatile 读了吗?

    我已经用“技术上”的回答避开了这个问题。您指的是readValueUnderLock 方法。根据 JMM,非最终字段的写入,无论是内联构造还是在构造函数内,都可以在对象可用于另一个线程后发布。

    所以

    public Entry{
     volatile Object value;
     public Entry(Object v){
         value = v;
     }
    }
    
    Thread 1
    Entry e = new Entry(new Object());
    
    Thread 2
    if(e != null)
        Object value =  e.value; // here, according to the JMM, value can be null.  
                                 // If value were final it would never be null
    

    读取锁下的值会将读取与来自其他线程的先前写入同步,这样将始终防止出现空值。

    总而言之,这种情况要么极不可能,要么在 x86 架构下是不可能的。

    【讨论】:

    • 另见 Brian Goetz 在 ConcurrentHashMap 上的文章 ibm.com/developerworks/java/library/j-jtp08223
    • @greyfairer 我不明白 get 如何获取锁。其次,如果线程 1 获得一些值并开始对该值进行一些更改,并且并行线程 2 更新相同元素的值线程1使用,那么不会导致不一致。
    • @NikhilArora 是的。抱歉,如果那是您的问题,我一定错过了阅读。在对象put 进入地图之前发生的所有操作在每个后续get 之后都是可见的。除非该对象也是线程安全的,否则在 put 之后对该对象的所有操作都会受到竞争条件的影响。
    • @greyfairer 完美的答案和链接。谢谢。我已接受您的回答。
    猜你喜欢
    • 1970-01-01
    • 2018-11-18
    • 1970-01-01
    • 1970-01-01
    • 2022-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多