Is iterating over a list retrieved in a synchronized block thread-safe?

痴心易碎 提交于 2020-02-04 04:56:51

问题


I am a bit confused regarding one pattern I have seen in some legacy code of ours.

The controller uses a map as a cache, with an approach that should be thread safe, however I am still not confident it indeed is. We have a map, which is properly synchronized during addition and retrieval, however, there is a bit of logic outside of the synchronized block, that does some additional filtering. (the map itself and the lists are never accessed outside of this method, so concurrent modification is not an issue; the map holds some stable parameters, which basically never change, but are used often).

The code looks like the following sample:

public class FooBarController { 

    private final Map<String, List<FooBar>> fooBarMap = 
                    new HashMap<String, List<FooBar>>();

    public FooBar getFooBar(String key, String foo, String bar) {

        List<FooBar> foobarList;

        synchronized (fooBarMap) {
            if (fooBarMap.get(key) == null) {
                foobarList = queryDbByKey(key);
                fooBarMap.put(key, foobarList);

            } else {
                foobarList = fooBarMap.get(key);
            }
        }

        for(FooBar fooBar : foobarList) {
            if(foo.equals(fooBar.getFoo()) && bar.equals(fooBar.getBar()))
                return fooBar;
        }

        return null;
    }

    private List<FooBar> queryDbByKey(String key) {

        // ... (simple Hibernate-query) 
    } 

    // ... 
}

Based on what I know about the JVM memory model, this should be fine, since if one thread populates a list, another one can only retrieve it from the map with proper synchronization in place, ensuring that the entries of the list is visible. (putting the list happens-before getting it)

However, we keep seeing cases, where an entry expected to be in the map is not found, combined with the typical notorious symptoms of concurrency issues (e.g. intermittent failures in production, which I cannot reproduce in my development environment; different threads can properly retrieve the value etc.)

I am wondering if iterating through the elements of the List like this is thread-safe?


回答1:


The code you provided is correct in terms of concurrency. Here are the guarantees:

  • only one thread at a time adds values to map, because of synchronization on map object
  • values added by thread become visible for all other threads, that enter synchronized block

Given that, you can be sure that all threads that iterate a list see the same elements. The issues you described are indeed strange but I doubt they're related to the code you provided.




回答2:


It could be thread safe only if all access too fooBarMap are synchronized. A little out of scope, but safer may be to use a ConcurrentHashmap.

There is a great article on how hashmaps can be synchronized here.




回答3:


  1. In situation like this it's best option to use ConcurrentHashMap.
  2. Verify if all Update-Read are in order.
  3. As I understood from your question. There are fix set of params which never changes. One of the ways I preferred in situation like this is:

    I. To create the map cache during start up and keep only one instance of it.

    II. Read the map Instance anytime anywhere in the application.




回答4:


In the for loop you are returning reference to fooBar objects in the foobarList.

So the method calling getFooBar() has access to the Map through this fooBar reference object.

try to clone fooBar before returning from getFooBar()



来源:https://stackoverflow.com/questions/35083000/is-iterating-over-a-list-retrieved-in-a-synchronized-block-thread-safe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!