Java 表达方式是;a==1?1:0“;用比较加三元运算符表示原子?

Java 表达方式是;a==1?1:0“;用比较加三元运算符表示原子?,java,c++,pthreads,atomic,Java,C++,Pthreads,Atomic,快速提问?这条线是用C++和java编写的吗? class foo { bool test() { // Is this line atomic? return a==1 ? 1 : 0; } int a; } 如果有多个线程访问该行,我们最终可能会执行检查 首先a==1,然后更新a,然后返回,对吗 补充:我没有完成课程,当然,还有其他部分更新了…不,它仍然是一个测试,然后是一个集合,然后是一个返回 是的,多线程将是一个问题 只是语法上的糖 return a==1

快速提问?这条线是用C++和java编写的吗?
class foo {
  bool test() {
    // Is this line atomic?
    return a==1 ? 1 : 0;
  }

  int a;
}
如果有多个线程访问该行,我们最终可能会执行检查 首先a==1,然后更新a,然后返回,对吗


补充:我没有完成课程,当然,还有其他部分更新了…

不,它仍然是一个测试,然后是一个集合,然后是一个返回

是的,多线程将是一个问题

只是语法上的糖

return a==1 ? 1 : 0;
这是一种简单的写作方式

if(a == 1)
    return 1;
else
    return 0;

我看不到任何更新a的代码。但是我想你可以理解。

< P>不,对于C++和java都是。 在Java中,您需要使方法
同步
,并以同样的方式保护
a
的其他用途。确保在所有情况下都在同一对象上同步


在C++中,你需要使用保护<代码> A<代码>,可能用来确保在你的函数结束时正确地解锁互斥体。

< P>正确的方法简单地说:不!(适用于Java和C++)

一个不太正确但更实际的答案是:技术上这不是原子的,但在大多数主流体系结构中,至少是C++。 您发布的代码中没有修改任何内容,只测试变量。因此,代码通常会导致单个

测试
(或类似)指令访问该内存位置,也就是说,原子指令。该指令将读取缓存线,并且在相应的loaction中会有一个定义良好的值,不管它是什么

然而,这是偶然的,不是你可以信赖的

当另一个线程向该值写入数据时,它通常甚至可以正常工作,这也是偶然发生的。为此,CPU获取缓存线,覆盖缓存线内相应地址的位置,并将整个缓存线写回RAM。测试变量时,获取一个缓存线,该缓存线包含旧值或新值(两者之间没有任何内容)。在任何类型的保证之前都不会发生,但是你仍然可以考虑这个“原子”。 当多个线程同时修改该变量(不是问题的一部分)时,情况要复杂得多。为了使其正常工作,您需要使用C++11中的一些内容,或者使用原子内部结构,或者类似的东西。否则,就很不清楚会发生什么,操作的结果可能是什么——一个线程可能会读取值,增加值并写回,但另一个线程可能会在写回修改后的值之前读取原始值。

这或多或少保证在所有当前平台上都会很糟糕。

< P>不管有没有写,读C++中的非原子类型的值是<强>不<强>原子操作。如果没有写入,那么您可能不关心它是否是原子的;如果其他线程可能正在修改该值,那么您当然会在意。

您的问题可以重新表述为:is语句:

   a == 1
原子还是非原子?不,它不是原子的,您应该使用std::atomic作为一个函数,或者在某种锁下检查该条件。如果整个三元运算符是原子的或不是原子的,在本文中并不重要,因为它不会改变任何东西。如果您在问题中的意思是,在本规范中:

bool flag = somefoo.test();

标志与A==1一致,如果你的问题中的全三元运算符是原子的,则它是不相关的。< /P> < P>不,它不是原子的(一般的),虽然它可以在某些体系结构中(例如C++中,如果整数是对齐的,除非它强迫它不对齐),否则< /P> 考虑以下三个线程:

// thread one:                // thread two:             //thread three
while (true)                  while (true)               while (a) ;
   a = 0xFFFF0000;               a = 0x0000FFFF;
如果对
a
的写入不是原子的(例如,英特尔If
a
未对齐,并且为了便于讨论,两条连续缓存线中的每一条都有16位)。现在,虽然第三个线程似乎永远无法退出循环(a
的两个可能值都不是零),但事实是赋值不是原子的,线程2可以将较高的16位更新为0,线程3可以在线程2获得完成更新的时间之前将较低的16位读取为0,从循环中走出来


整个条件与问题无关,因为返回的值是线程的本地值。

这里有很多很好的答案,但没有一个提到Java中需要将
a
标记为
volatile


如果没有使用其他同步方法,但其他线程可以更新
a
,则这一点尤为重要。否则,您可能正在读取旧值
a

请考虑以下代码:

bool done = false;

void Thread1() {
  while (!done) {
    do_something_useful_in_a_loop_1();
  } 
  do_thread1_cleanup();
}

void Thread2() {
  do_something_useful_2();
  done = true;
  do_thread2_cleanup();
}
这两个线程之间的同步是使用布尔变量done完成的。这是同步两个线程的错误方法

在x86上,最大的问题是编译时优化

编译器可以将do_something_有用的_2()的部分代码移到“done=true”下面。 编译器可以将do_thread2_cleanup()的部分代码移到“done=true”上方。 如果dou\u something\u有用的\u在\u a\u loop\u 1()中没有修改“done”,编译器可能会按以下方式重新写入Thread1:

  if (!done) {
    while(true) {
      do_something_useful_in_a_loop_1();
    } 
  }
  do_thread1_cleanup();
所以Thread1永远不会退出

在x86以外的体系结构上,缓存效应或无序指令执行可能会导致其他微妙的问题

大多数种族检测器将检测这种种族

此外,大多数动态竞争检测器将报告打算与此bool同步的内存访问上的数据竞争

(即,在“做一些有用的事情”和“做线程1”和“清理”)之间)


要解决这种竞争,您需要使用编译器和/或内存障碍(如果您不是专家,只需使用锁)。

a
不会以任何方式更新,那么为什么
synchronized
会有帮助呢?@JoachimIsaksson理论上这个类有更多内容,但是的,有