2 回答
TA贡献1785条经验 获得超4个赞
根据定义,只读操作是线程安全的。仅当至少有一个线程修改数据时,才会发生竞争条件或数据竞争。
所以如果将值放入映射中,在创建从映射中读取值的线程之前,则操作是完全安全的。如果你从不修改 bar 或其在 foo 中的引用,你也应该没问题。在这种情况下,您甚至不需要并发映射。一张普通的地图就可以了。
但是,如果 bar 被修改或者对 bar 的引用在 foo 中被修改,你可能会得到意想不到的结果。这是一个可能出错的例子。让我们假设 bar 是一个long.
class Foo {
public long bar;
}
你有线程 1 在做:
Foo foo = concurrentMap.get("key");
.....
..... /// some code
System.out.println(foo.bar);
后台还有另一个线程可以执行此操作:
Foo foo = concurrentMap.get("key");
.....
long newBar = foo.bar + 1;
foo.bar = newBar;
在这里,您有一个直接的竞争条件。
现在,如果线程 2 实际上只是这样做:
Foo foo = concurrentMap.get("key");
.....
long newBar = rand.nextLong();
foo.bar = newBar;
您没有竞争条件,但您有数据竞争,因为 long 是 64 位,编译器可能会执行对 long 和 double 的赋值作为两个操作。
还有更多可能出错的场景,如果不非常小心,真的很难对它们进行推理
所以至少,你应该让 bar 变得不稳定,像这样:
class Foo {
public volatile Object bar;
}
并且要非常小心你如何操作 bar 以及里面的东西,如果它是某个类的实际对象。
如果您想了解有关数据竞争的更多信息并更深入地了解竞争条件,您应该查看这个优秀的课程https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=汇率
它有一个关于这个的部分,用非常好的例子很好地解释了它。
您还应该查看官方 Java 内存模型文档https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4
我希望它有帮助
TA贡献1830条经验 获得超9个赞
它应该作为将值放入和取出并发哈希图应该建立发生之前的关系(请参阅ConcurrentHashMap.get() 是否保证通过不同的线程看到先前的 ConcurrentHashMap.put() ?)。消费者线程应该看到在地图插入之前的 bar 值。
就我个人而言,我会努力使 Foo(和 bar)对象不可变(如果它们永远不会改变,为什么不呢?)
添加回答
举报