Java并行易失性i ++

问题描述 投票:3回答:3

我有一个全局变量

volatile i = 0;

和两个线程。每个都做以下事情:

i++;
System.out.print(i);

我收到以下组合。 12,21和22。

我理解为什么我没有得到11(挥发性不允许缓存i)和 我也理解12和22。

我不明白的是如何获得21?

如何获得这种组合的唯一可能方法是,稍后打印的线程必须是第一个将i从0增加到1然后缓存i==1的线程。然后另一个线程将i从1增加到2然后打印2.然后第一个线程打印缓存的i==1。但我认为volatile不允许缓存。

编辑:运行代码10,000次后,我得到11次。将volatile添加到i并不会改变可能的组合。

markspace是对的:volatile禁止缓存ii++不是原子的。这意味着i在增量期间仍会在寄存器中被“缓存”。

r1 = i
//if i changes here r1 does not change
r1 = r1 + 1 
i = r1

这就是11仍然可能的原因。引起21是因为PrintStreams不同步(参见Karol Dowbecki的回答)

java multithreading increment runnable race-condition
3个回答
3
投票

不幸的是++不是原子操作。尽管volatile不允许缓存,但允许JVM读取,递增,然后作为单独的操作写入。因此,您尝试实现的概念是不可行的。你需要使用synchronized作为它的互斥体,或者使用类似AtomicInteger的东西来提供原子增量操作。


6
投票

您的代码不保证首先调用System.out的线程。

由于i关键字,volatile的增量和读数按顺序发生,但打印没有。


1
投票

唯一可行的方法是,后来打印的线程必须是第一个将i从0递增到1然后缓存i == 1的线程...

你忘记了System.out.print(i);的作用:该语句调用System.out对象的print(...)方法,该方法使用i在调用开始时存储的任何值。

所以这是可能发生的一种情况:

Thread A
increments i  (i now equals 1)
Starts to call `print(1)`  //Notice! that's the digit 1, not the letter i.
gets bogged down somewhere deep in the guts...

          Thread B
          increments i (i=2)
          Calls `print(2)`
          Gets lucky, and the call runs to completion.

Thread A
finishes its `print(1)` call.

两个线程都没有缓存i变量。但是,System.out.print(...)函数对你的volatile int i一无所知。它只知道传递给它的值(1或2)。

© www.soinside.com 2019 - 2024. All rights reserved.