今天在頭條上看到一道據說是騰訊的筆試題,閑來無事,准備驗證一下!
題目如下:
有一個變量int a=0;兩個線程同時進行+1操作,每個線程加100次,不加鎖,最后a的值是()?
根據我的理解答案不唯一,最大是兩百,最小是2吧。直接上代碼:
class MyThread implements Runnable { static volatile long i = 0; public void run() { for (int m = 0; m < 100; m++) { i++; } } }; public class RunnableTest { public static void main(String[] args) throws InterruptedException { MyThread mt = new MyThread(); Thread t1 = new Thread(mt); Thread t2 = new Thread(mt); t1.start(); t2.start(); Thread.sleep(500); System.out.println(MyThread.i); } }
結果一臉懵逼,每次都是兩百,我都開始有點懷疑人生了。
考慮之后發現可能是100太小了,樣本空間不夠(偽學霸),吧100改成10000,奇跡出現了
果然大部分時候i的值小於20000.
具體解釋一下原因(復制過來的哈哈):
i++不是原子(就是只由一條指令構成的)操作,也就是說,它不是單獨一條指令,而是3條指令:
1、從內存中把i的值取出來放到CPU的寄存器中
2、CPU寄存器的值+1
3、把CPU寄存器的值寫回內存
假設兩個線程的執行步驟如下:
1. 線程A執行第一次i++,取出內存中的i,值為0,存放到寄存器后執行加1,此時CPU1的寄存器中值為1,內存中為0;
2. 線程B執行第一次i++,取出內存中的i,值為0,存放到寄存器后執行加1,此時CPU2的寄存器中值為1,內存中為0;
3. 線程A繼續執行完成第99次i++,並把值放回內存,此時CPU1中寄存器的值為99,內存中為99;
4. 線程B繼續執行第一次i++,將其值放回內存,此時CPU2中的寄存器值為1,內存中為1;
5. 線程A執行第100次i++,將內存中的值取回CPU1的寄存器,並執行加1,此時CPU1的寄存器中的值為2,內存中為1;
6. 線程B執行完所有操作,並將其放回內存,此時CPU2的寄存器值為100,內存中為100;
7. 線程A執行100次操作的最后一部分,將CPU1中的寄存器值放回內存,內存中值為2;