是否遍历在同步块中检索的列表是线程安全的? [英] Is iterating over a list retrieved in a synchronized block thread-safe?

查看:80
本文介绍了是否遍历在同步块中检索的列表是线程安全的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于在我们的一些旧代码中看到的一种模式,我有些困惑.

控制器使用映射作为缓存,其方法应该是线程安全的,但是我仍然不确定确实如此.我们有一个映射,该映射在添加和检索期间已正确同步,但是,在同步块之外还有一些逻辑,它会进行一些附加过滤. (地图本身和列表永远不会在此方法外部访问,因此并发修改不是问题;地图拥有一些稳定的参数,这些参数基本上从不更改,但经常使用).

代码类似于以下示例:

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) 
    } 

    // ... 
}

根据我对JVM内存模型的了解,这应该没问题,因为如果一个线程填充了一个列表,那么另一个线程只能在适当的同步下从映射中检索它,从而确保列表的条目是可见的. (将列表放入在发生之前 )

但是,我们不断看到一些案例,其中未找到预期出现在映射中的条目,并伴有并发问题的典型臭名昭著的症状(例如,生产中的间歇性故障,我无法在开发环境中重现;不同的线程;可以正确检索值等.

我想知道遍历这样的List元素是否是线程安全的吗?

解决方案

您提供的代码在并发方面是正确的.这是保证:

  • 由于地图对象上的同步,一次只能有一个线程向地图添加值
  • 由线程添加的值对于进入同步块的所有其他线程均可见

鉴于此,您可以确保迭代列表的所有线程看到相同的元素.您描述的问题确实很奇怪,但我怀疑它们与您提供的代码有关.

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?

解决方案

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.

这篇关于是否遍历在同步块中检索的列表是线程安全的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆