java - 如果一个方法只是调用 Java 中的另一个线程安全方法,它是否是线程安全的?

标签 java multithreading thread-safety

我想知道如果一个方法只是调用另一个线程安全方法,它是否是线程安全的。我有一个这样的例子。正如文档所述,ConcurrentSkipListSet 是线程安全的。

public class MessageHolder {

    private Set<String> processedIds;

    public MessageHolder() {
        this.processedIds = new ConcurrentSkipListSet<>();
    }

    public void add (String id) {
        processedIds.add(id);
    }

    public boolean contains (String id) {
        return processedIds.contains(id);
    }

    public void remove (String id) {
        processedIds.remove(id);
    }

}

你可能会问为什么我不直接使用ConcurrentSkipListSet。原因是我想为此处执行的操作创建一个界面,此示例将类似于内存版本。

最佳答案

我认为您应该对评论进行一些澄清,并对导致竞争条件的原因进行一些澄清。

关于竞争条件——所有线程的基本思想是,在执行的任何时候,您所在的当前线程都可以重新安排到稍后的时间,或者另一个线程可能正在并行执行和访问相同的数据。

首先,如前所述,进程 ID 应该是最终的。除非您这样做,否则您的代码将不是线程安全的。尽管 ConcurrentSkipListSet<> 是线程安全的,但这并不能阻止变量 processIds 被另一个线程重新分配。另外,Java 很奇怪,您的 processIds 字段必须标记为 Final,以保证在构造函数完成之前对其进行初始化。我发现这篇 stackoverflow 文章解释了 java 中对象构造的一些问题,以供更多阅读。 Constructor synchronization in Java 。基本上,不要将构造函数字段标记为同步,但如果您想保证构造函数中的变量初始化(在本例中是这样做的),则将您的字段标记为final。

要回答您关于这是否是线程安全的问题,唯一的答案是这取决于您期望的客户端使用情况。您提供的方法对于其编写的预期目的确实是线程安全的,但客户端可以使用它们并产生竞争条件。您关于是否 100% 有必要使用同步关键字的直觉是正确的。然而,这些评论也暗示,如果不使这些方法显式地线程安全,可能会在将来对代码的可维护性和正确性产生一些可怕的后果。

客户端仍然可以以不安全的方式使用您提供的 API,这可能会导致评论之一中提到的竞争条件。如果您向客户端提供接口(interface),您可能不关心这一点...或者您可能关心这一点并希望为客户端提供一种机制,以便在保证线程安全的情况下对您的类进行多次访问。

总的来说,我可能会建议您将方法标记为同步,原因有几个:1)它让客户端清楚地知道他们正在访问线程安全的方法,这一点非常重要。我可以很容易地想象这样一种情况:客户端在不需要锁时决定使用锁,但会损害性能。 2)将来有人可能会更改您的方法以包含一些需要同步关键字的不同逻辑(这并不是不太可能,因为您似乎已经处于线程环境中)。

关于java - 如果一个方法只是调用 Java 中的另一个线程安全方法,它是否是线程安全的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56735112/

相关文章:

java - 需要验证使用 mvc :annotation-driven? 定义处理程序映射的旧方法

java - 将 GUI 添加到我的应用程序

java - 在 MainActivity 中调用 stopService() 时线程不会停止

java - Java 1.4 中 Lock.tryLock() 的替代方案

c# - C#++ 运算符在 foreach 循环中是否成为线程安全的?

c++ - boost::lexical_cast 线程安全吗?

java - DrawerItemClickListener 无法解析为类型

java - java中的素数测试

c++ - 锁定取消引用的互斥量是不好的行为吗?

java - 非线程安全代码意外地给出可重复的结果