Java 哈希映射同步

Java 哈希映射同步,java,multithreading,hashmap,synchronized,Java,Multithreading,Hashmap,Synchronized,我有一个任务来同步HashMap的方法put(K键,V值)。但是它应该比synchronized(this)或synchronized(table)工作得更快。我写了这段代码: public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.

我有一个任务来同步HashMap的方法
put(K键,V值)
。但是它应该比
synchronized(this)
synchronized(table)
工作得更快。我写了这段代码:

public V put(K key, V value) {
    if (key == null) 
        return putForNullKey(value);

    int hash = hash(key.hashCode());
    int i = indexFor(hash, table.length);

    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            synchronized(e) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
    }

    modCount++;
    addEntry(hash, key, value, i);
    return null;
}
public V put(K键,V值){
if(key==null)
返回putForNullKey(值);
int hash=hash(key.hashCode());
int i=indexFor(散列,table.length);
for(条目e=表[i];e!=null;e=e.next){
对象k;
如果(e.hash==hash&((k=e.key)==key | | key.equals(k))){
已同步(e){
V oldValue=e.value;
e、 价值=价值;
e、 记录存取(本);
返回旧值;
}
}
}
modCount++;
加法器(散列、键、值、i);
返回null;
}

问题是,当不同线程使用同一个键调用此方法时,可能会出现以下情况:
map.entrySet().size()>1
。所以我的同步是错误的,但我不明白为什么。如何正确地执行此操作?

您的
for
循环遇到的一个问题是迭代可能发生变化的集合


我真的建议,或者使整个方法(或其中的大部分)同步化

您的
for
循环在一个可能发生变化的集合上迭代的一个问题


我真的建议,或者使整个方法(或大部分方法)
同步

,因为您的e可以在进入同步块之前更改, 完整的表可以在同步块迭代之前和迭代期间更改

问题是要锁定表条目。 因此,最好将完整的方法声明为synchronized

使用信号量(请参阅Java并发API)确保对表的原子访问

因评论而更新:

为应用程序提供缓存机制。引入附加列表(队列、缓存) 它在表或映射被阻止时收集所有传入的更改。 所以你的申请仍然可以继续。直到桌子不再被阻塞,你才能 将缓存项添加到表中


答案只是一个建议。

因为您的e可以在进入同步块之前更改, 完整的表可以在同步块迭代之前和迭代期间更改

问题是要锁定表条目。 因此,最好将完整的方法声明为synchronized

使用信号量(请参阅Java并发API)确保对表的原子访问

因评论而更新:

为应用程序提供缓存机制。引入附加列表(队列、缓存) 它在表或映射被阻止时收集所有传入的更改。 所以你的申请仍然可以继续。直到桌子不再被阻塞,你才能 将缓存项添加到表中

答案只是一个建议

问题是,当不同线程使用同一个键调用此方法时,可能会出现以下情况:map.entrySet().size()>1。所以我的同步是错误的,但我不明白为什么

我不是100%确定我理解这个错误案例,但是我看到您尝试同步时出现了一些问题,这些问题将导致竞争条件

例如,如果两个线程试图同时向
HashMap
添加同一个键,它们将在bucket中循环,而找不到条目。然后,他们两个都将尝试将条目添加到表中。这可能导致两个条目具有相同的键,甚至导致表损坏

我还假设,
addEntry(…)
可以改变bucket数组,这可能会导致所有元素的重新灰化。因此,当另一个线程处理过时的数组时,一个线程可能会导致存储桶发生变化。这可能会导致条目丢失或表再次损坏

我怎样才能正确地做到这一点

这是一个非常重要的练习。您可以做的一件事是使用
ReadWriteLock
,这样至少可以让查找不会阻止写入。但是,如果您正在向映射中写入一个新条目,则可能必须对其进行独占锁定,因为上面提到的bucket数组发生了更改

问题是,当不同线程使用同一个键调用此方法时,可能会出现以下情况:map.entrySet().size()>1。所以我的同步是错误的,但我不明白为什么

我不是100%确定我理解这个错误案例,但是我看到您尝试同步时出现了一些问题,这些问题将导致竞争条件

例如,如果两个线程试图同时向
HashMap
添加同一个键,它们将在bucket中循环,而找不到条目。然后,他们两个都将尝试将条目添加到表中。这可能导致两个条目具有相同的键,甚至导致表损坏

我还假设,
addEntry(…)
可以改变bucket数组,这可能会导致所有元素的重新灰化。因此,当另一个线程处理过时的数组时,一个线程可能会导致存储桶发生变化。这可能会导致条目丢失或表再次损坏

我怎样才能正确地做到这一点


这是一个非常重要的练习。您可以做的一件事是使用
ReadWriteLock
,这样至少可以让查找不会阻止写入。但是,如果要在地图中写入新条目,则可能需要对其进行独占锁定,因为上面提到的bucket数组发生了更改。

Like?我不能用它。这是一种教育任务。A必须同步通用HashMap,但它应该比集合工作得更快;你所说的“情况,那map.entrySet().size()>1”和“同步错误”是什么意思?你能给出详细信息或示例吗?如果
table.lengt