【问题标题】:Is a Collection threadsafe if it's only written in the constructor?如果 Collection 只写在构造函数中,它是线程安全的吗?
【发布时间】:2014-06-17 12:13:43
【问题描述】:

假设我们有这个类

final class Foo {
    private final Set<String> bar = new HashSet<>();

    public Foo() {
        bar.add("one");
        bar.add("two");
        bar.add("three");
    }

    public boolean contains(final String s) {
        return bar.contains(s);
    }
}

实例化Foo 并从多个线程调用此对象的contains 是否是线程安全的?

  1. 对集合的引用是privatefinal。没有人可以直接访问该集合。
  2. 唯一的写访问发生在构造函数中
  3. 构造函数执行后,集合只读取不修改。

如果没有,是否有纯 Java 替代 Guava 的不可变集合?

【问题讨论】:

标签: java concurrency


【解决方案1】:

您的Foobar 实际上是不可变的。它是线程安全的。

【讨论】:

  • 谢谢,但HashSet 不是一成不变的,只是对它的引用。
  • @Vertex 你是对的。比方说,给你一个对象foo,用你的代码,你怎么能改变内部集合bar?如果您的 bar 是“仅适用于”的东西,那么它是线程安全的。
【解决方案2】:

这取决于如何发布对该对象的访问。虽然 bar Set 是最终的,因此保证对所有线程可见,但不能保证在构造函数结束之前发生映射的填充。

但是,无论对象是如何创建和提供的,都可以保证它是线程安全的。

private final Set<String> bar;

public Foo() {
    bar = new HashSet<String>(Arrays.asList("one", "two", "three"));
}

Testing initialization safety of final fields

https://stackoverflow.com/a/23995782/676877

【讨论】:

    【解决方案3】:

    它是线程安全的,前提是

    1) 构造函数在完全构造之前不会泄漏引用。

    2) 没有人可以访问该集合。

    3) 不能创建可以编辑集合的子类。

    不过,作为一般规则,如果您想实现这一点,请使用来自 guava 的不可变集合,这会使行为对程序员明确,然后返回整个映射是安全的。我认为在纯 java 中你可以返回一个不可修改的集合视图。

    【讨论】:

      【解决方案4】:

      只要以只读方式访问,我认为您应该是安全的。此外,java 提供了通过 Collections 类上的静态方法公开的其基本集合的不可变版本,因此我将查看 Collections.unmodifiableSet()

      另外,当您在示例中添加字符串时,字符串本身在 java 中是不可变的,如果您添加了可变对象然后决定从不同的线程修改/读取它们(您需要同步块在此案子)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-10
        • 1970-01-01
        相关资源
        最近更新 更多