【问题标题】:Concurrent access of List of Map in JavaJava中Map List的并发访问
【发布时间】:2017-12-10 19:30:08
【问题描述】:

我有以下列表。

 List<Map<String,String>> input = new ArrayList<Map<String,String>>();

我有一个要求,在线程中访问 List 中的每个 Map 对象。 保证没有两个线程访问同一个映射对象。

for(Map<String,String> input : inputList){
    triggerThread(input); // Thread modifies the input. It adds elements in the input map
}

我需要使用 synchronizedList(inputList) 或 synchronizedmap(input) 还是可以直接使用输入映射而不需要任何同步逻辑?哪个设计好?

这可能是基本问题。我对如何实施感到很困惑。

   class Main{
      public static void main(String[] args){
       List<Map<String,String>> inputList = new ArrayList<Map<String,String>>();
       Map<String,String> input = new HashMap<String,String>();
       input.put("attr1","param1");
       inputList.add(input);
       ExecutorService pool= Executors.newFixedThreadPool(10);
       for(Map<String,String> input : inputList){
         RequestHandler request = new RequestHandler(input);
         pool.submit(request);
        }
      }
   }

     class RequestHandler extends Runnable{
         Map<String,String> input;
         RequestHandler(Map<String,String> input){
           this.input=input;
         }
        @Override
         public void run() {
           //writing logic vaguely
           input.add("attr2", "param2");
        }
    }

【问题讨论】:

  • 你不能在循环遍历列表时修改它,你会得到一个 ConcurrentModificationException。为此使用迭代器。您仍然需要同步。
  • 示例代码从不修改列表,因此永远不会抛出 ConcurrentModificationException。
  • 这里我只是在修改列表中的对象。我没有在列表中删除或添加元素。所以我们可能不会得到 ConcurrentModificationException。你能帮我同步列表吗?
  • @Teja,你能把triggerThread ()的代码贴出来吗?
  • 它还取决于您何时、何地以及如何创建相对于从中读取的线程的列表。如果不查看您的所有代码,我认为我们无法准确回答您的问题。见stackoverflow.com/help/mcve

标签: java multithreading concurrency java-threads


【解决方案1】:

假设如下:

  • inputList 列表由一个线程填充,然后不会被其他线程修改(即不添加/删除元素)
  • input 映射中没有两个线程访问同一个映射对象(如问题所述)

如果是这种情况,则无需在inputListinput 上进行同步。

另一方面,如果您将通过其他线程访问inputList 本身,例如使用Collections.syncrhonizedList()

【讨论】:

  • 这并不总是正确的,例如,如果列表是在阅读器线程启动之后创建和/或填充的,并且不是最终的,或者是最终的但在分配后填充。请参阅17.4.517.4.417.5
  • 因为我没有更改 inputList 并且只修改列表元素引用的对象,这些元素在其他地方不可见,所以我觉得 Hinson 的建议在这种情况下有效。在这种情况下,我不需要使用任何同步逻辑。
  • 我强烈觉得在这种情况下没有必要使用同步逻辑。由于对地图的修改不会影响列表大小。我通常将它想象为 Map 在不同的内存中,并且它的内存地址存储在列表中。但是如果这是正确的行为是否会影响列表大小,我有点怀疑。将元素添加到两个列表对象的映射中,是否会导致列表出现任何同步问题。
  • @Teja:向地图添加新元素这一事实不会改变 inputList。 inputList 包含地图的references(或指针)。这些参考不会改变(只有底层地图会改变)。
【解决方案2】:

由于列表永远不会改变并且实际上是不可变的,因此您唯一关心的是 Map 元素。一个线程对 Map 元素所做的更改可能对另一个线程不可见。要解决这个问题,您可以使用 ConcurrentHashMap:

例如:

    List<Map<String,String>> input = new ArrayList<>();
    input.add(new ConcurrentHashMap<>());
    ... 

作为参考,请查看this,尤其是关于可见性的部分

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-04
    • 1970-01-01
    • 2022-07-31
    • 1970-01-01
    • 2021-02-23
    相关资源
    最近更新 更多