[TOC]
并發(fā)三大特性
原子性投慈、可見性、有序性
1. 原子性
含義
一個(gè)或多個(gè)操作拷淘,要么全部執(zhí)行且在執(zhí)行過程中不被任何因素打斷各墨,要么全部不執(zhí)行。
在 Java 中启涯,對(duì)基本數(shù)據(jù)類型的變量的讀取和賦值操作是原子性操作贬堵。
重要
不采取任何的原子性保障措施的自增操作并不是原子性的。
如何保證原子性
- 通過 synchronized 關(guān)鍵字定義同步代碼塊或者同步方法保障原子性结洼。
- 通過 Lock 接口保障原子性黎做。
- 通過 Atomic 類型保障原子性。
2. 可見性
含義
當(dāng)一個(gè)線程修改了共享變量的值松忍,其他線程能夠看到修改的值蒸殿。
Java 內(nèi)存模型是通過在變量修改后將新值同步回主內(nèi)存,在變量讀取前從主內(nèi)存刷新變量值這種依賴主內(nèi)存作為傳遞媒介的方法來實(shí)現(xiàn)可見性的鸣峭。
如線程 A 修改一個(gè)普通變量的值宏所,然后向主內(nèi)存進(jìn)行回寫,另外一條線程 B 在線程 A 回寫完成了之后再從主內(nèi)存進(jìn)行讀取操作摊溶,新變量的值才會(huì)對(duì)線程 B 可見爬骤。
可見性問題
舉個(gè)例子:
// 線程1執(zhí)行的代碼
int i = 0;
i = 10;
// 線程2執(zhí)行的代碼
j = i;
假若執(zhí)行線程 1 的是 CPU1,執(zhí)行線程 2 的是 CPU2 莫换。由上面的分析可知霞玄,當(dāng)線程 1 執(zhí)行 i =10 這句時(shí)骤铃,會(huì)先把 i 的初始值加載到 CPU1 的高速緩存中,然后賦值為 10 坷剧,那么在 CPU1 的高速緩存當(dāng)中 i 的值變?yōu)?10 了惰爬,卻沒有立即寫入到主存當(dāng)中。
此時(shí)線程 2 執(zhí)行 j = i惫企,它會(huì)先去主存讀取i的值并加載到 CPU2 的緩存當(dāng)中撕瞧,注意此時(shí)內(nèi)存當(dāng)中i的值還是 0,那么就會(huì)使得j的值為 0 狞尔,而不是 10风范。
這就是可見性問題,線程 1 對(duì)變量 i 修改了之后沪么,線程2沒有立即看到線程 1 修改的值硼婿。
如何保證可見性
- 通過 volatile 關(guān)鍵字標(biāo)記內(nèi)存屏障保證可見性。
- 通過 synchronized 關(guān)鍵字定義同步代碼塊或者同步方法保障可見性禽车。
- 通過 Lock 接口保障可見性寇漫。
- 通過 Atomic 類型保障可見性。
- 通過 final 關(guān)鍵字保障可見性
3. 有序性
含義
即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行殉摔。
JVM 存在指令重排州胳,所以存在有序性問題。
如何保證有序性
- 通過 synchronized關(guān)鍵字 定義同步代碼塊或者同步方法保障可見性逸月。
- 通過 Lock接口 保障可見性栓撞。