- 可見性
定義: 一個線程對共享變量做了修改之后摊阀,其他的線程立即能夠看到(感知到)該變量這種修改(變化)。
Java 內存模型 是以 主存踪蹬、線程私有內存(工作內存) 組成的胞此,通過工作內存修改后的值同步到主存中,在讀取變量前從主內存刷新最新值到工作內存中跃捣,這種依賴主內存的方式來實現(xiàn)可見性的漱牵。
- 原子性
定義:一個操作不能被打斷,要么全部執(zhí)行完畢疚漆,要么不執(zhí)行
java內存模型所保證的是酣胀,同線程內,所有的操作都是由上到下的娶聘,但是多個線程并行的情況下闻镶,則不能保證其操作的有序性。
- 有序性
定義:在本線程內觀察丸升,操作都是有序的铆农;如果在一個線程中觀察另外一個線程,所有的操作都是無序的狡耻。
java內存模型所保證的是墩剖,同線程內猴凹,所有的操作都是由上到下的,但是多個線程并行的情況下岭皂,則不能保證其操作的有序性郊霎。
計算機在執(zhí)行程序時,為了提高性能蒲障,編譯器個處理器常常會對指令做重排歹篓,一般分為以下 3 種:
- 編譯器優(yōu)化指令重排
- 指令并行重排
- 內存系統(tǒng)重排
單線程環(huán)境里面確保程序最終執(zhí)行的結果和代碼執(zhí)行的結果一致
處理器在進行重排序時必須考慮指令之間的數據依賴性
多線程環(huán)境中線程交替執(zhí)行,由于編譯器優(yōu)化重排的存在揉阎,兩個線程中使用的變量能否保證用的變量能否一致性是無法確定的庄撮,結果無法預測
Volatile
- 只能修飾變量
- 禁止指令重排(編譯器優(yōu)化指令重排)
- 保證可見性
- 不保證原子性
- 不會阻塞線程
它所修飾的變量不保留拷貝,直接訪問主內存中的毙籽。
在Java內存模型中洞斯,有main memory,每個線程也有自己的memory (例如寄存器)坑赡。為了性能烙如,一個線程會在自己的memory中保持要訪問的變量的副本。這樣就會出現(xiàn)同一個變量在某個瞬間毅否,在一個線程的memory中的值可能與另外一個線程memory中的值亚铁,或者main memory中的值不一致的情況。 一個變量聲明為volatile螟加,就意味著這個變量是隨時會被其他線程修改的徘溢,因此不能將它cache在線程memory中。
Synchronized
- 修飾代碼塊或方法
- 會被編譯器優(yōu)化指令重排
- 保證原子性也間接保證了可見性(因為它會將私有內存中和公共內存中的數據做同步)
- 可能會阻塞線程
當兩個并發(fā)線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時捆探,一個時間內只能有一個線程得到執(zhí)行然爆。另一個線程必須等待當前線程執(zhí)行完這個代碼塊以后才能執(zhí)行該代碼塊。
當一個線程訪問object的一個synchronized(this)同步代碼塊時黍图,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊曾雕。
當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞助被。
當一個線程訪問object的一個synchronized(this)同步代碼塊時剖张,它就獲得了這個object的對象鎖。結果揩环,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞修械。
最后:
線程安全包含原子性和可見性兩個方面,Java的同步機制都是圍繞這兩個方面來確保線程安全的检盼。
關鍵字volatile主要使用的場合是在多個線程中可以感知實例變量被修改肯污,并且可以獲得最新的值使用,也就是多線程讀取共享變量時可以獲得最新值使用。
關鍵字volatile提示線程每次從共享內存中讀取變量蹦渣,而不是私有內存中讀取哄芜,這樣就保證了同步數據的可見性。但是要注意的是:如果修改實例變量中的數據
例如:i++柬唯,也就是i=i+1认臊,則這樣的操作其實并不是一個原子操作,也就是非線程安全的锄奢。表達式i++操作步驟分解如下:
1)從內存中取出i的值失晴。
2)計算i的值;
3)將i的值寫到內存中拘央。
假如在第2步計算值得時候涂屁,另外一個線程也修改i的值,name這個時候就會出現(xiàn)臟讀數據灰伟。解決的辦法就是使用synchronized關鍵字拆又。 所以說volatile本身并不處理數據的原子性,而是強制對數據的讀寫及時的影響到主內存中栏账。