繁体   English   中英

JAVA-并发和锁

[英]JAVA - Concurrency and locks

我需要创建一个像这样的哈希图:

Map<String, List<Integer>>

我正在使用2个线程。

这是输出映射的示例:

---------------------
| xxx | a  b  c  d  |
| yyy | a' b' c' d' |
| zzz | A  B  C  D  |
---------------------

这两个线程都有助于创建条目。 因此,例如,线程1具有要存储的以下数据集:

-----------------------------------
| 1 |  (xxx,a)  (yyy,a')  (zzz,A) |
| 2 |  (xxx,b)  (yyy,b')  (zzz,B) |
-----------------------------------

线程2具有:


| 1 |  (xxx,c)  (yyy,c')  (zzz,C) |
| 2 |  (xxx,d)  (yyy,d')  (zzz,D) |
-----------------------------------

因此,同时,两个线程可以计算相同的密钥:线程1(xxx,a)和线程2(xxx,c)。

我可以使用的最佳锁定/解锁机制是什么?

我尝试了以下

ReadWriteLock lock = new ReentrantReadWriteLock();

executor.submit(() -> {
   lock.writeLock().lock();
   try {
      sleep(1);
      map.put("foo", "bar");
  } finally {
     lock.writeLock().unlock();
  }

});

但我不想锁定整个哈希图。 我想创建一个锁IF,并且仅在处理期间两个线程应该在同一个键上工作时才创建。

谢谢

首先,您应该使用并发Map:

Map<String, List<Integer>> map = new ConcurrentHashMap<>();

其次, 被多个线程访问之前,应使用同步列表填充它,如下所示:

for (String key : keys) {
    map.put(key, Collections.synchronizedList(new ArrayList<>()));
}

或者您可以像这样及时地进行操作:

List<Integer> list = map.get(key);
if (list == null) {
    map.putIfAbsent(key, Collections.synchronizedList(new ArrayList<>()));
    list = map.get(key);
}

而已。 现在一切都应该是线程安全的。

注意:重要的是不要在从地图中获取列表之前使用列表。 由于并发,一个以上的线程可能会尝试将丢失的列表添加到映射中,但是只有其中一个会成功。 因此,尝试添加丢失的列表后,请始终从地图中获取该列表。

不确定其他答案是否会满足您的要求,因为您需要从地图上进行读取(以查看键是否已存在),然后以原子操作进行更新。

不知道这是否是最优雅的解决方案,但是您可以使用类似的方法...

导入java.util.HashSet; 导入java.util.Map; 导入java.util.Set; 导入java.util.concurrent.ConcurrentHashMap;

public class KeyLockMap<K, V> {

    private Map<K, V> map;

    private Set<K> lockKeys;

    public KeyLockMap() {
        map = new ConcurrentHashMap<K, V>();
        lockKeys = new HashSet<K>();
    }

    public void lock(K key) throws InterruptedException {
        synchronized(lockKeys) {
            // Keep waiting while the lockKeys set contains the key we want to update
            while(lockKeys.contains(key)) {
                lockKeys.wait();
            }
        }
    }

    public void unlock(K key) {
        synchronized(lockKeys) {
            lockKeys.remove(key);
            // Notify any threads that may be waiting for this key to be removed
            lockKeys.notifyAll();
        }
    }

    public Map<K, V> getMap() {
        return map;
    }
}

现在在您的线程中,您可以执行...

keyLockMap.lock(KEY);
Map<String, List<Integer>> map = keyLockMap.getMap();
...do updates...
keyLockMap.unlock(KEY);

希望这可以帮助。


另一种可行的方法是在ConcurrentHashMap上使用“ putIfAbsent”,例如...

List<Integer> newValue = new CopyOnWriteArrayList<>();
List<Integer> value = concurrentMap.putIfAbsent(KEY, newValue);

if(value == null) {
    value = newValue;
}

value.add(DATA);

如果另一个线程首先执行putIfAbsent ,则将获得该线程创建的CopyOnWriteArrayList ,并且可以向其中添加数据,因为CopyOnWriteArrayList是线程安全的。

(已修改为考虑到在首次认沽权时返回空值的原因...)

无需重新设计轮子, java.util.concurrent包已经包含Map接口的线程安全实现-ConcurrentHashMap

但是,您还必须同步您的列表(请参见下面的BretC注释)。 因此,最方便的解决方案可能是使用Guava同步多图 像那样:

public static void main(String[] args) {

    Multimap <String, Integer> map = Multimaps.synchronizedMultimap(HashMultimap.<String, Integer> create());

    // In Thread 1
    map.put("foo", 1);

    //In Thread 2
    map.put("foo", 2);

    System.out.println(map); // prints {foo=[1, 2]}
}

您应该使用Hashtable而不是Hashmap。 Hashtable是线程安全的(已经同步),不需要再次实现锁定/解锁机制。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM