我有一个全局变量
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禁止缓存i
但i++
不是原子的。这意味着i
在增量期间仍会在寄存器中被“缓存”。
r1 = i
//if i changes here r1 does not change
r1 = r1 + 1
i = r1
这就是11仍然可能的原因。引起21是因为PrintStreams不同步(参见Karol Dowbecki的回答)
不幸的是++
不是原子操作。尽管volatile
不允许缓存,但允许JVM读取,递增,然后作为单独的操作写入。因此,您尝试实现的概念是不可行的。你需要使用synchronized
作为它的互斥体,或者使用类似AtomicInteger
的东西来提供原子增量操作。
您的代码不保证首先调用System.out
的线程。
由于i
关键字,volatile
的增量和读数按顺序发生,但打印没有。
唯一可行的方法是,后来打印的线程必须是第一个将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)。