java - 迭代同步块(synchronized block)中检索的列表是线程安全的吗?

标签 java multithreading thread-safety

我对在我们的一些遗留代码中看到的一种模式有点困惑。

Controller 使用 map 作为缓存,其方法应该是线程安全的,但我仍然不确定它确实是安全的。我们有一个映射,它在添加和检索期间正确同步,但是,在同步块(synchronized block)之外有一些逻辑,它会进行一些额外的过滤。 ( map 本身和列表永远不会在该方法之外访问,因此并发修改不是问题; map 拥有一些稳定的参数,这些参数基本上不会改变,但经常使用)。 p>

代码类似于以下示例:

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 内存模型的了解,这应该没问题,因为如果一个线程填充一个列表,另一个线程只能在适当同步的情况下从映射中检索它,从而确保列表的条目是可见的。 (将列表发生在获取之前)

但是,我们不断看到这样的情况:未找到预期在映射中的条目,再加上并发问题的典型臭名昭著的症状(例如,生产中的间歇性故障,我无法在我的开发环境中重现;不同的线程可以正确检索值等)

我想知道像这样迭代列表的元素是否是线程安全的?

最佳答案

您提供的代码在并发性方面是正确的。以下是保证:

  • 由于映射对象上的同步,一次只有一个线程向映射添加值
  • 线程添加的值对于进入同步块(synchronized block)的所有其他线程都可见

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

关于java - 迭代同步块(synchronized block)中检索的列表是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35083000/

相关文章:

java - 启动画面错误

C++:条件变量等待

Java servlet 方法 HashMap 范围

ios - CoreSpotlight 默认索引线程安全

java - Google 电子表格 key 版本

java - 如何在 Java 中将 byte[] 转换为 String?

ruby - Thread#run 和 Thread#wakeup 之间的区别?

java - 手动触发一个@Scheduled 方法

scala - Scala 中仅更新单个 var 线程的类实例安全吗?

java - 如何在for循环中为两个不同的值断言true?