我回复了 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 提供的任何机制来确保安全发布。即在构造函数中设置的同步、volatile 或final 字段。
因此,可以想象在您的示例中,对 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/