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

原子值可以在"

刘升
2023-03-14

我知道下一个场景:(奇怪的格式,我知道)

private final AtomicBoolean aBoolean = new AtomicBoolean(true);

public void doSomething() {
    if (
        aBoolean.get()                       // line A
            &&                               // line B
        aBoolean.compareAndSet(true, false)  // line C
        ) {
        System.out.println("Was true!")
    }
}

如果线程#1和线程#2在完全相同的时间输入do某物(),这将发生:

>

两者都将执行"

CMPXCHG指令同时对两个线程生效:

3.1锁定前缀本机使用

3.2线程#1或#2首先到达,赢得比赛。

3.3获胜线程比较(是aBoolean==true?)这将返回"true",因此一个布尔值将被设置为"false"。

3.4 aBoolean现在为false。

3.5线程丢失比较(aBoolean==真吗?)这将返回“false”,从而使任何进一步的操作短路。

获奖线程将打印“是真的!”。

在丢失线程的视角下,行A中的第一个aBoolean.get()是......

现在,假设执行可以在运算符之间发生,就像上面的例子中所做的那样,让我们为第二种场景添加第二种方法:

public void unluckySet() {
    aBoolean.set(false);
}

假设线程#3恰好到达并执行unluckySet()

如果获胜的线程到达“线B”,则表示它到达了“线A”,aBoolean为“真”。

我的问题是:

CMPXCHG是否会将更新的值正确读取为"false"?,这意味着. set()也由与compareAndSet()相同的锁持有。

在并发和线程之间:

不使用运算符(“


共有1个答案

麹繁
2023-03-14

Java内存模型是在没有数据竞争(您的程序没有数据竞争)的情况下的顺序一致性。这个很强,;它表示程序中的所有读写操作形成一个总的顺序,这与程序顺序一致。因此,您可以想象,来自不同线程的读写只是以某种方式交错或混洗在一起,而不会改变来自同一线程的操作的相对顺序。

但出于这个目的,每个动作都是这个顺序中的一个独立元素。所以仅仅是因为aBoolean这个事实。get()aBoolean。compareAndSet()是两个操作而不是一个操作,其他线程可能会在它们之间执行任意数量的其他操作。

不管这些动作是单个语句的一部分,还是不同的语句;或者它们出现在什么样的表情中;或者他们之间有什么运营商(如果有);或者线程本身可能会或可能不会在它们周围进行什么计算。两个动作“如此紧密地联系在一起”是不可能的,除非用语言定义为原子的单个动作来代替它们,否则在这两个动作之间不会发生任何其他事情。

在机器级别,发生这种情况的一个非常简单的方法是,由于aBoolean.get()aBoolean.compareAndSet()几乎肯定是两个不同的机器指令,中断可能会到达在他们之间。这个中断可能会导致线程延迟任何时间,在此期间其他线程可以做任何他们想做的事情。所以线程#1和#2完全有可能在它们的get()compareAndSet()之间中断,并且线程#3同时执行它的set

注意:对特定机器可能如何工作的推理通常有助于理解为什么不希望的行为是可能的,如前一段所述。但是它不能替代关于形式记忆模型的推理,也不应该被用来试图论证一个程序必须有它想要的行为。即使你心目中的特定机器会为你的代码做正确的事情,或者你想不出一个可信的机器会失败的方法,这也不能证明你的程序是正确的。

所以试图说“哦,机器会做一个锁cmpxchg,所以等等等等,一切正常”是不明智的;你没有想到的其他机器可能会以完全不同的方式工作,这仍然符合抽象的Java内存模型,但在其他方面违反了基于x86的期望。事实上,x86是一个特别糟糕的例子:由于历史原因,它提供了一组相当强的指令级内存排序保证,而许多其他“弱排序”架构没有,因此可能有许多事情抽象地Java允许,但x86在实践中不行。

 类似资料:
  • 通常,对于,(或)作为读-修改-写操作不是原子操作。但是我经常看到编译器(例如GCC)为它生成以下代码(请在这里尝试): 既然与相对应的第5行是一条指令,那么我们是否可以断定在本例中是原子的呢? 更新 请注意,这个问题不是增量是否是原子的(它不是,这过去是,现在也是这个问题的开场白)。关键是在特定场景中是否可以,即在某些情况下是否可以利用单指令性质来避免前缀的开销。而且,正如关于单处理器机器的一节

  • 假设以下两种计数器实现: 乍一看,原子应该更快、更具可扩展性。我相信他们是的。但是它们始终比同步块快吗?或者当该规则被打破时,存在某些情况(例如SMP/单CPU机器、不同的CPU ISA、操作系统等)?

  • Solr4提供了对索引中现有文档进行原子(部分)更新的功能。即。可以匹配文档ID并替换一个字段的内容,或者向多值字段添加更多条目:http://wiki.apache.org/solr/atomicupdates 原子更新可以从DataImportHandler(DIH)完成吗?

  • 问题内容: 我有几个在单个WebLogic集群中运行的J2EE应用程序实例。 在某些时候,这些应用程序会进行合并以将记录插入或更新到后端Oracle数据库中。MERGE检查是否存在具有指定主键的行。如果在那里,请更新。如果没有,请插入。 现在,假设有两个应用程序实例要插入或更新主键= 100的行。假设该行不存在。在合并的“检查”阶段,他们都看到行不在此处,因此他们都试图插入。然后,我得到了唯一的键

  • 正如在C 11中所知,有6个内存顺序,在关于std::memory\u order\u acquire的文档中: http://en.cppreference.com/w/cpp/atomic/memory_order memory\u order\u acquire内存\u顺序\u获取 具有此内存顺序的加载操作对受影响的内存位置执行获取操作:在此加载之前,当前线程中的内存访问不能重新排序。这可以

  • 不可变类型是很简单的:一旦被创建,它们就是常量。如果你验证构造对象参数,你知道从那以后它们就是有效的状态。你不可能改变对象的内部状态让它失效。一旦对象构造好,如果不允许任何状态改变,你会省去很多必须的错误的检查。不可变类型本质上是线程安全的:多个读取者可以访问相同的内容。如果内部状态不会改变,不同线程就没有机会读取到不一致的数据。不可变类型可以让你的对象安全地暴露。调用者不能修改你的对象的内部状态