在多线程情况下,有i变量:
volatile int i = 0;
这种情况下,如果多线程对i进行自增操作:i++,结果可能会与预期值不同,这是什么原因呢?
先来看看大多人对这一错误的解释:
是因为volatile不能保证原子性,而i++其实有三步操作,读,修改,写,并不是原子操作,所以volatile当然不能保证结果的准确性,解释完毕!
其实博主我觉得这种解释相当的流氓
让我想起高中时略带痞气的我应付语文老师的抽问,大概是:
一个选择题,下列选项正确的是:
A.李白 B.杜甫 C.陶渊明 D.汪伦 这道题的答案老师已经说了选C,老师问我:张珂,这道题为什么不选B。
我的回答:因为B是错的。
感谢大家对这一问题应付性的解释,让我回忆起了高中生涯。。。。
这不是废话吗?为什么,人家问的是原因,看着网上博客上的那些解释我真的来气……..
算了,让我来仔细推敲一下,具体是什么原因。
什么是原子性?
下面贴上砖家们的非常详细的解释:
原子性
(1)原子是构成物质的基本单位(当然电子等暂且不论),所以原子的意思代表着——“不可分”;
(2)原子性是拒绝多线程操作的,不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。例如 a=1是原子性操作,但是a++和a +=1就不是原子性操作。
ok,现在我明白了,i++不具备原子性,所以呢,在执行i++的时候,可能会有其他线程让i++执行到一半断掉。
ok,哥今天就跟你杠上了,于是哥再次确认了i++的过程。
i++的过程如下:
1.生成临时变量: int temp;
2.将i的值拷贝给临时变量: temp = i;
3.i加1: i = i + 1;
现在假设有2个线程都执行i++操作(如下代码片所示),照大家所说,因为i++不是原子操作,i被volatile修饰(即保证内存可见性)的情况下,假设Ⅰ线程先执行,无论Ⅰ线程执行到哪一步被打断,Ⅱ线程再执行最后都是到i = i+1;的时候才会改变i的值,而且i的值一旦改变,对另一个线程立马就可见了,i的值永远是最新的,那为什么i++会出错呢?
Ⅰ线程: Ⅱ线程:
int temp; ① int temp; ④
temp = i; ② temp = i; ⑤
i = i + 1; ③ i = i + 1; ⑥
唉,看了一下午,网上都没有解释清楚的,问周围人也都不清楚,等大神解答!万分感激!!!
分割线
·································································································································································
更新于 2018.2.1
突然想起 i=i+1;这也不是原子操作啊。。 也就是说 如果上面I线程的代码的 i=i+1 右边的i+1先计算了,还没有来得及赋值给 i 的时候,Ⅰ线程中断,Ⅱ线程更新i的值,这个时候虽然i的值会重新刷新到 Ⅰ 线程中,但是i+1早就被计算了,我猜想 i+1的值应该也是存在某个中间变量中,所以即使 Ⅰ 线 程 立马刷新 i 的值, 但是这个存 i+1 值的中间变量是不会更新的,所以才会造成 volatile修饰的整型变量自增会发生运算错误。
关于 i=i+1; 这个i+1计算了之后是否有个中间变量暂时存着还不知道,有待大佬们求证。或者说i+1的值计算结束是放在常量池了?然后i=i+1;的这个赋值操作,实际上就是i重新指向常量池中的值。应该就是这两种可能,具体哪一种未知,大佬们求证!!!!