2

来自 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 read。如果易失性读取导致未命中,则在最后一次尝试成功读取时获得该段的锁定。这是什么意思?什么是易失性读取?

4

1 回答 1

3

我读过迭代不持有锁,那么上面的语句是什么意思?

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

get之类的操作也可以锁吗?还请提供场景。

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

Java 8 正在发布一个新的 CHM 实现,所以它可能很快就会过时。

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

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

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

除了更新之外还有其他情况可以完成锁定吗?

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

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

我已经用我的“技术上”回答避开了这个问题。你指的是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 架构下是不可能的。

于 2013-09-05T11:41:51.150 回答