java線程執(zhí)行過程中改變量值的結(jié)果引起的思考
注:筆者在研究volatile關(guān)鍵詞的使用時(shí),意外發(fā)現(xiàn)了以下四種案例情況,感覺頗為神奇,于是進(jìn)行記錄。限于筆者才疏學(xué)淺,無法暫時(shí)無法更進(jìn)一步研究問題的本質(zhì)與底層的內(nèi)容,所以希望能夠拋磚引玉,得來更多更優(yōu)秀深入的見解。
1.第一種情況
尋常情況下,線程執(zhí)行過程中突然對其變量進(jìn)行變動(dòng)
這是最尋常的問題,run方法中將active設(shè)為true后,while一直用的就是這個(gè)true,不會(huì)去內(nèi)存中刷新這個(gè)值,也就導(dǎo)致了while死循環(huán)。
線程:

測試:


2.第二種情況
對變量使用volatile關(guān)鍵字修飾情況下,線程執(zhí)行過程中突然對其變量進(jìn)行變動(dòng)
加上volatile關(guān)鍵字后,每次使用到active變量都將拿到最新的值,所以stop方法改active的值后,while就能拿到最新的active值為false,然后結(jié)束循環(huán)。
注:(源自https://www.runoob.com/java/java-modifier-types.html的volatille關(guān)鍵字解釋)
volatile 修飾的成員變量在每次被線程訪問時(shí),都強(qiáng)制從共享內(nèi)存中重新讀取該成員變量的值。而且,當(dāng)成員變量發(fā)生變化時(shí),會(huì)強(qiáng)制線程將變化值回寫到共享內(nèi)存。這樣在任何時(shí)刻,兩個(gè)不同的線程總是看到某個(gè)成員變量的同一個(gè)值。
線程:

測試:


3.第三種情況
這種情況比較特殊,在while循環(huán)中加入輸入打印語句下,線程執(zhí)行過程中突然對其變量進(jìn)行變動(dòng)
線程:

測試:

那么為什么輸出語句能導(dǎo)致run方法的while循環(huán)發(fā)現(xiàn)active已經(jīng)被修改了呢?因?yàn)閜rintln里有用到了synchronized這個(gè)同步關(guān)鍵字。
如圖:

而synchronized將導(dǎo)致(解釋源自:https://blog.csdn.net/qq_28082757/article/details/101065531):
獲得同步鎖;
清空工作內(nèi)存;
從主內(nèi)存拷貝對象副本到工作內(nèi)存;? ?
執(zhí)行代碼(計(jì)算或者輸出等);
刷新主內(nèi)存數(shù)據(jù);
釋放同步鎖。
所以真正的起作用的不是輸出語句,而是synchronized,把輸出語句換成什么都沒做的同步區(qū)塊也能實(shí)現(xiàn)相同的效果。
如圖:

但是,這又產(chǎn)生了另一個(gè)問題:為什么在Stop方法里用到輸出語句(也是內(nèi)部的synchronized起作用),不會(huì)影響while這個(gè)方法的active讀取呢?難道作用的只是這個(gè)方法內(nèi)數(shù)據(jù)刷新,而不是整個(gè)線程?
我將stop方法中的輸出語句放在active修改后,想著是否能夠因?yàn)閟ynchronized的數(shù)據(jù)刷新使得while發(fā)現(xiàn)active已經(jīng)變了,于是停下來,然而并沒有。
如圖:

那么,這又是為什么呢?
因?yàn)檎麄€(gè)運(yùn)行過程存在兩個(gè)線程,一個(gè)是main線程,一個(gè)volatileTestThread2線程。
兩個(gè)線程都有一個(gè)active的變量的副本在它們的工作內(nèi)存中(真正的active是放在主內(nèi)存中)
run方法在volatileTestThread2內(nèi)調(diào)用運(yùn)行,所以synchronized影響的是volatileTestThread2的工作內(nèi)存里變量的與主內(nèi)存之間的刷新
stop方法是main線程調(diào)用的,所以synchronized是影響main線程的工作內(nèi)存與主內(nèi)存之間的刷新。
二者是不同的。
這也就解釋了“為什么在Stop方法里用到輸出語句(也是內(nèi)部的synchronized起作用),不會(huì)影響while這個(gè)方法的active讀取呢?”這個(gè)問題。

4.第四種情況
在while循環(huán)中加入Sleep下,線程執(zhí)行過程中突然對其變量進(jìn)行變動(dòng)
線程:

測試:

我們都知道,Sleep將使得線程進(jìn)入休眠(等待狀態(tài)),操作系統(tǒng)調(diào)度程序不調(diào)度睡眠線程以接收CPU時(shí)間。
那么等待狀態(tài)結(jié)束后,線程進(jìn)入就緒狀態(tài)以及被調(diào)度選中后執(zhí)行的這個(gè)過程,線程是否是進(jìn)行了內(nèi)存變量的刷新?或者是進(jìn)行了某些底層處理,導(dǎo)致產(chǎn)生了和前文synchronized使用后一樣的結(jié)果?
正在研究中...

這是筆者的github的測試項(xiàng)目地址:https://github.com/17lhf/happyTest
可以查看完整的代碼。