当前位置: 首页 > 知识库问答 >
问题:

易失性变量的读-修改-写操作如何做到线程安全

单于飞鸣
2023-03-14

我正在阅读JCIP,无法理解3.3.1中的以下语句,

对共享的易失性变量执行读-修改-写操作是安全的,只要可以确保仅从单个线程写入易失性变量。在本例中,您将修改限制在单个线程中,以防止出现争用情况,并且volatile变量的可见性保证确保其他线程看到最新的值。

即使挥发性变量只从单个线程写入,它怎么能不提高竞争条件?如果线程A在计数器为1时执行计数器,线程B可以在计数器1写入内存之前进入,并获得计数器的旧值1。这是如何确保“防止竞争条件”和“其他线程看到最新的值”的??

P. S.我知道这里有同样的问题,但我没有找到任何令人满意的答案。

共有1个答案

厍胤运
2023-03-14

易失性变量的读-修改-写操作如何做到线程安全

一般来说,他们不能。

你引用的文字说:

"对共享的易失性变量执行读-修改-写操作是安全的,只要您能确保仅从SINGLE线程写入易失性变量。"

这不是线程安全的意思。作为一般性声明。

即使volatile变量只从一个线程写入,它怎么能不引发争用条件呢?

读取线程没有竞争条件,因为它们只会看到变量最近写入的值。而写线程是由正常的执行规则控制的,因此线程和自身之间不存在竞态条件。

如何确保“防止种族状况”?

见上图。只有在有多个线程更新的情况下,才能进行一场竞赛。从竞赛条件的定义来看,这是不言而喻的。

[和]“其他线程可以看到最新的值”?

这是Java内存模型指定的关于volatile的结果。对易失性变量的读取保证可以看到前一次写入的值。。。无论如何。

如果线程A在计数器为1时执行计数器操作,线程B可能会在计数器1被写回内存之前进入,并获得计数器的陈旧值1。

在这种情况下,不存在竞争条件。线程B看到最近写入的计数器的值。计数器尚未更新。这意味着,count不是原子的。但是线程安全和原子性并不是一回事。原子能是更有力的保证。JLS明确指出,count不是挥发性物质的原子。

现在(显然)如果应用程序中有多个共享的易失性变量由不同的(单个)线程更新,那么前面几段中的简单推理可能会失败。

 类似资料:
  • 最近我在读一些关于java并发的书。关于线程安全,如果不可能使一个类变为inmutable,那么可以通过同步它的数据来确保线程安全。 下面的类显然不是线程安全的 然后我可以同步写,但它不会保持线程安全 因为我不仅需要同步写入,还需要同步读取 现在的问题是,通过使用易失性,我可以保证其他线程会看到更新的值,所以这让我认为这个类应该是线程安全的 最后一个类线程安全吗??

  • tcp套接字是具有双向读写功能的endpoint。在java中,我们可以获得套接字的InputStream和OutputStream。 同时使用这些流是否安全? 据我所知,有一个连接能够在任何给定时间从一个endpoint发送或接收到其他数据。 我正在基于SocketChannels实现nio传输层,我想保留一个线程用于所有写入,一个线程用于接受和读取,但我不确定如果我的线程同时尝试在同一个套接字

  • 官方记录说 写入易失性字段与监视器释放具有相同的记忆效果,从易失性字段读取与监视器获取具有相同的记忆效果。 和 有效地,挥发性的语义学得到了实质性的加强,几乎达到了同步的水平。为了可见性的目的,挥发性字段的每次读取或写入都像“半”次同步。 从这里开始。 这是否意味着,对volatile变量的任何写入都会使执行线程将其缓存刷新到主存中,而每次从volatile字段读取都会使线程从主存重新读取其变量?

  • 根据Go博客, 地图对于并发使用是不安全的:它没有定义当您同时读写地图时会发生什么。如果需要从并发执行的goroutines读取映射和向映射写入映射,则访问必须通过某种同步机制进行调解。(来源:https://blog.golang.org/go-maps-in-action) 有人能详细说明一下吗?跨例程的并发读取操作似乎是允许的,但是如果尝试读取和写入同一个键,并发读取/写入操作可能会生成竞争

  • 所有的中断函数都能正常工作,但是过程函数却让我很生气。 我会感激任何我没注意的把戏。

  • 问题内容: 我尝试了解为什么此示例是正确同步的程序: 由于存在冲突的访问(存在对a的写入和读取),因此在每个顺序一致性中,必须在访问之间的关系之前执行。假设顺序执行之一: 是1发生-在2之前发生,为什么? 问题答案: 不,在相同变量的易失性写入之前(以同步顺序),在易失性写入 之前 不一定 会发生 易失性读取。 这意味着它们可能处于“数据争用”中,因为它们“冲突的访问未按先发生后关系进行排序”。如