发生在线程和原子变量之间

时间:2018-11-18 16:28:19

标签: java concurrency atomic volatile java.util.concurrent

假设AtomicInteger c在两个线程(线程1和线程2)之间共享。 Thread1使用t1设置(仅一次)易失性变量c.incrementAndGet()。 Thread2使用t2设置(仅一次)易失变量c.incrementAndGet()。一旦设置了t1和t2,就不会再由其他任何线程对其进行设置。假设线程1设置了t1之后,它检查t2的值并得到null。是否可以保证随后将t2设置为比t1高的值? (反之亦然)。换句话说,下面的断言是否总是正确的?那是为什么呢?

AtomicInteger c = new AtomicInteger();
volatile Integer t1=null;
volatile Integer t2=null;

//Thread1
t1=c.incrementAndGet();
if(t2 == null){
  assert t2==null || t2>t1;
}

//Thread2
t2=c.incrementAndGet();
if(t1==null){
  assert t1==null || t1>t2;
}

我认为这些断言是正确的,其原因如下:如果为t1分配了c的增量值,并且尚未为t2分配c的增量值,那么当随后为t2分配c的值时,t2必须为大于t1的值。

更新:由于根据断言下面的正确答案可能并不总是成立,因此我添加了第2部分问题:签出Happens before between threads and atomic variable Part 2

2 个答案:

答案 0 :(得分:1)

不,它们并不总是正确的。不能保证线程1将在线程2之前运行,或者操作不会交织。如果它们以以下方式运行:

  1. 线程2分配t2 = 1
  2. 线程2执行其if检查,其结果为true
  3. 线程1分配t1 = 2
  4. 线程2进行断言

...然后在步骤3中,线程2将看到t1 != nullt2 > t1

线程1同样可能失败。

(正如JB Nizet所提到的,即使我上面写的操作实际上也包含多个操作。对于这个问题,严格地讲细节级别不是必需的,但是 是真正将事情分解成各自的操作,例如increasAndAndGet和它所涉及的分配。经验可以让您在想显示为什么不起作用的情况下对它们进行过滤,但要表明它将会起作用,您确实需要考虑每个操作。)

答案 1 :(得分:1)

不,不能保证。可能会发生以下情况:

  • thread2:c.incrementAndGet(c为1,t2仍为null,但稍后将用1初始化)
  • thread1:c.incrementAndGet(c为2,t1仍为null,但稍后将用2初始化)
  • 线程1:t1 = 2
  • 线程1:如果(t2 == null):条件为true。 if块被评估
  • 线程2:t2 = 1
  • 线程1:t2 ==空:条件为假,因此对or的另一个操作数进行求值
  • 线程1:t2> t1:由于t2为1而t1为2,所以为false
  • 线程1:断言:失败