阅读“实践中的Java并发性”,第3.5节包含以下内容:
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
除了创建两个的明显的线程安全隐患外Holder
,该书还声称可能会发生发布问题。
此外,对于Holder
诸如
public Holder {
int n;
public Holder(int n) { this.n = n };
public void assertSanity() {
if(n != n)
throw new AssertionError("This statement is false.");
}
}
一个AssertionError
可以扔!
这怎么可能?我能想到的唯一允许这种荒谬行为的方法是,如果Holder
构造函数不被阻塞,那么当构造函数代码仍在另一个线程中运行时,将创建对实例的引用。
这可能吗?
之所以可行,是因为Java的内存模型较弱。它不保证读写顺序。
可以通过以下代表两个线程的两个代码片段来重现此特定问题。
线程1:
someStaticVariable = new Holder(42);
线程2:
someStaticVariable.assertSanity(); // can throw
表面上看来这不可能发生。为了理解为什么会发生这种情况,您必须超越Java语法,并降低到更低的水平。如果您看一下线程1的代码,它实际上可以分解为一系列的内存写和分配:
由于Java的内存模型较弱,因此从线程2的角度来看,代码完全有可能按以下顺序实际执行:
害怕?是的,但是有可能发生。
但是,这意味着线程2现在可以assertSanity
在n
获得值42
之前调用它。在操作#3完成之前,在操作#3完成之后,在操作之后一次都n
可以读取该值两次assertSanity
,因此可以看到两个不同的值并引发异常。
。
编辑
据乔恩斯基的AssertionError
migh仍然发生的Java8除非该场决赛。
问题内容: Spring对象是线程安全的吗?如果没有,如何使它们线程安全? 问题答案: 这是两个不相关的问题: spring线程安全吗? 没有。 Spring具有不同的bean 作用域(例如Prototype,Singleton等),但是所有这些作用域都是在创建bean 时强制执行的。例如,每次“注入”一个“原型”范围的bean都会被创建,而一个“单个”范围的bean将被创建一次并在应用程序上下文
类OneValueCache是不可变的。但是我们可以更改变量缓存的引用。 但我不能理解为什么VolateCachedFactorizer类是线程安全的。 对于两个线程(线程A和线程B),如果线程A和线程B同时到达,那么两个线程A和B都将尝试创建OnEvalueCache。然后线程A到达而线程B同时到达。然后线程A将创建一个,它覆盖threadB创建的值(OneValueChange是不可变的,但是
在我的应用程序中,我使用多个线程来处理客户端连接。 我在调试时发现了一个非常奇怪的行为——我有一个SelectionKey,通过调用(使用调试器)它的interestTops()方法,返回值是1(READ),但当我将数据发送到与该键对应的套接字时,选择器不会被唤醒。。 如果使用调试器,我将特定选择键更改为1(即使是1),选择器会突然对该更改做出反应。 在给定的时间内,我只有一个线程处理一个连接,但
问题内容: 我注意到,如果我尝试在循环内使用goroutines将其追加到切片,那么在某些情况下我会丢失/空白数据: 有时,当我从中打印all 时,某些元素是空字符串(),而在另一些情况下,in中则不存在某些元素。 我的代码是否存在数据争用,是否意味着对于多个goroutine并发使用来说不是线程安全的? 问题答案: 在Go中,没有值可以安全地进行并行读/写操作,切片(切片头)也不例外。 是的,您
我得到的是它们是线程安全的。 杰里米·曼森博客的片段- 因为this引用存储在lastconstructed中“,因此转义构造函数 请建议。
问题内容: 如果我有两个多个线程访问HashMap,但要保证它们永远不会同时访问同一密钥,那是否还会导致争用情况? 问题答案: 在@dotsid的回答中,他说: 如果你以任何方式更改HashMap,则代码将被破坏。 他是正确的。即使线程使用的是不相交的键集,在没有同步的情况下更新的HashMap也会中断。这是一些可能出错的事情。 如果一个线程执行put,则另一线程可能会看到哈希图大小的陈旧值。 当