java - 如何破坏这个(非?)线程安全对象?

标签 java multithreading thread-safety

我回复了 question较早前关于线程安全的内容并没有得到明确的答案(我认为)。

所以我一直试图通过让数千个线程读取和写入该对象来说服自己设计被破坏(可见性)——但我没有得到任何意想不到的东西。这显然不能证明它是线程安全的,可能只是证明我自己的局限性!

我理解重新排序的风险,但我不知道它如何适用于这种情况,因为 bar() 方法中的 clone 实例是本地的,其字段的更改在使用 return 发布到外部世界之前完成,之后实例实际上是不可变的。因此查看返回对象的线程会看到它的 bar 字段已经设置为正确的值...

所以我的问题是: 什么样的代码 你能展示一段使用 IsItSafe 的代码,它可能导致 2 个线程查看给定 IsItSafe 实例的 bar 字段的不同值?

为了引用和阅读方便,我把代码复制到这里:

public class IsItSafe implements Cloneable {

    private int foo;
    private int bar;

    public IsItSafe foo(int foo) {
        IsItSafe clone = clone();
        clone.foo = foo;
        return clone;
    }

    public IsItSafe bar(int bar) {
        IsItSafe clone = clone();
        clone.bar = bar;
        return clone;
    }

    public int getFoo() {
        return foo;
    }

    public int getBar() {
        return bar;
    }

    protected IsItSafe clone() {
        try {
            return (IsItSafe) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new Error(e);
        }
    }
}

最佳答案

可见性 当您的程序写入变量的值缓存在 cpu 缓存 中而不是立即写入 RAM 时,可能会出现问题。出于这个原因,如果在 cpu A 上运行的线程 A 在没有正确同步的情况下写入一个值,并且线程 B 从 cpu B 读取这个值,他可能会在 RAM 中看到一个陈旧的值,而不是最新的值(仅存在于处理器 A 的 cpu 缓存中).

在给定的示例中,您没有使用 Java 提供的任何机制来确保安全发布。即在构造函数中设置的同步volatilefinal 字段

因此,可以想象在您的示例中,对 create clone 对象的引用变为可用,但写入克隆字段的值仍保留在 cpu 缓存中。在这种情况下,其他线程将无法读取最新值。

提供一些引用。看这个例子

class FinalFieldExample {
  final int x;
  int y;
  static FinalFieldExample f;
  public FinalFieldExample() {
    x = 3;
    y = 4;
  }

  static void writer() {
    f = new FinalFieldExample();
  }

  static void reader() {
    if (f != null) {
      int i = f.x;
      int j = f.y;
    }
  }
}

The class above is an example of how final fields should be used. A thread executing reader is guaranteed to see the value 3 for f.x, because it is final. It is not guaranteed to see the value 4 for y, because it is not final.

你提出的论点也适用于这个例子,不是吗?创建实例,在构造函数中设置字段等。但是它不是线程安全的,因为写入 y 的值不需要对其他线程可见。 (引用的示例来自 JSR 133(Java 内存模型)FAQ:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#reordering)

更新:您已要求提供演示问题的代码。我曾经问过一个类似的(更开放的)问题:How to demonstrate java multithreading visibility problems? 给出的代码示例的有趣之处在于,它在不同的 Java 次要版本、不同的操作系统以及使用客户端或服务器 jvm 时表现不同。在这方面,我发现样本非常有趣。 需要注意的重要一点是,今天 实际创建导致代码可见性问题的示例代码很可能是不可能的。然而,明年 cpu 生成可能会实现不同的缓存策略,突然出现问题。如果您遵循 Java 语言规范的指导方针,您就可以保存。

关于java - 如何破坏这个(非?)线程安全对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9633771/

相关文章:

multithreading - 为什么 Rust playground 不会为线程产生不同的结果?

c++ - Windows 上 C++ 中的 wxWidgets

ruby - 如何在 Ruby 中同时运行两个线程?

multithreading - 将堆栈数据传入和传出闭包

java - 两个线程引用了非同步类的非共享实例。线程问题?

java - Quarkus Keycloak 管理客户端

Java Hibernate在插入用户名和密码时获取实体ID并将其删除

java - Spring 应用的 Permgen 空间错误

java - Java中 future 的相关澄清

java - 如何暂停其他正在进行的流读取器线程