值可变的并发安全容器的线程安全

假设我有一个确保线程安全(检索的完全并发)的映射,其中检索ConcurrentHashMap<String, Foo>的值是一些可变对象


class Foo { 

    public Object bar;

}

Foo值只会由单个线程构造,添加到地图一次,然后永远不会修改。操作可能如下所示:


Foo foo = new Foo();

foo.bar = "Test";

concurrentMap.add("key", foo);

在一个单独的线程中,工作是通过查看地图中的值来执行的(假设这里预先正确设置了这些值)


System.out.println(concurrentMap.get("key").bar);

Foo::bar在这种情况下访问有什么问题吗?是否存在无法按预期执行的情况?


12345678_0001
浏览 186回答 2
2回答

九州编程

根据定义,只读操作是线程安全的。仅当至少有一个线程修改数据时,才会发生竞争条件或数据竞争。所以如果将值放入映射中,在创建从映射中读取值的线程之前,则操作是完全安全的。如果你从不修改 bar 或其在 foo 中的引用,你也应该没问题。在这种情况下,您甚至不需要并发映射。一张普通的地图就可以了。但是,如果 bar 被修改或者对 bar 的引用在 foo 中被修改,你可能会得到意想不到的结果。这是一个可能出错的例子。让我们假设 bar 是一个long.class Foo {&nbsp;&nbsp; &nbsp; public long bar;}你有线程 1 在做:Foo foo = concurrentMap.get("key");.......... /// some codeSystem.out.println(foo.bar);后台还有另一个线程可以执行此操作:Foo foo = concurrentMap.get("key");.....long newBar = foo.bar + 1;foo.bar = newBar;在这里,您有一个直接的竞争条件。现在,如果线程 2 实际上只是这样做:&nbsp;Foo foo = concurrentMap.get("key");&nbsp;.....&nbsp;long newBar = rand.nextLong();&nbsp;foo.bar = newBar;您没有竞争条件,但您有数据竞争,因为 long 是 64 位,编译器可能会执行对 long 和 double 的赋值作为两个操作。还有更多可能出错的场景,如果不非常小心,真的很难对它们进行推理所以至少,你应该让 bar 变得不稳定,像这样:class Foo {&nbsp;&nbsp; &nbsp; 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我希望它有帮助

慕标琳琳

它应该作为将值放入和取出并发哈希图应该建立发生之前的关系(请参阅ConcurrentHashMap.get() 是否保证通过不同的线程看到先前的 ConcurrentHashMap.put() ?)。消费者线程应该看到在地图插入之前的 bar 值。就我个人而言,我会努力使 Foo(和 bar)对象不可变(如果它们永远不会改变,为什么不呢?)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java