概述
?當(dāng)多個(gè)線程訪問某個(gè)類時(shí),不管運(yùn)行時(shí)環(huán)境采用何種調(diào)度方式,并且在主調(diào)代碼中不需要額外的同步,這個(gè)類都能表現(xiàn)出正確的行為,那么這個(gè)類就是線程安全的。線程安全體現(xiàn)在三個(gè)方面:原子性、可見性和有序性。
原子性
?一個(gè)操作是不可中斷的,要么全部執(zhí)行成功,要么全部執(zhí)行失敗。當(dāng)多個(gè)線程工作時(shí),一個(gè)操作一旦開始,就不會(huì)被其他線程干擾。
?Java中實(shí)現(xiàn)原子性操作的兩種方式:Atomic包 和 鎖
?Atomic是基于CAS實(shí)現(xiàn)的,下面以AtomicInteger為例。
AtomicInteger是含有一個(gè)int類型的值,這個(gè)類的作用是原子性的更新。事物都是一分為
二的,AtomicInteger雖然支持原子性的操作,但是其并不能代替Integer類。該類繼承自
Number。
以getAndSet(int newValue) 方法為例解釋。
該方法:原子性的賦新值,并且返回舊值。
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
該類調(diào)用了Unsafe類的getAndSetInt方法
Unsafe的getAndSetInt方法
public final int getAndSetInt(Object var1, long var2, int var4) {
int var5;
do {
//第一處
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var4));//第二處
return var5;
}
參數(shù)var1 就是傳入的AtomicInteger實(shí)例
參數(shù)var2 就是傳入的valueOffset值,valueOffset是屬性的偏移量,Unsafe就是通過valueOffset值獲取實(shí)際值。
參數(shù)var4 就是傳入的newValue,預(yù)設(shè)值的新值。
第一處 通過對(duì)象和偏移量,原子性的獲取了值。并且將這個(gè)值返回。
第二處 調(diào)用了compareAndSwapInt方法。CAS。設(shè)置成功之后 跳出循環(huán)。
compareAndSwapInt(var1, var2, var5, var4)原理
var5是舊值(期望的值), var4是新值, var1和var2配合可以求出內(nèi)存中的值
如果內(nèi)存值與var5(期望的值)相一致,那么就將內(nèi)存的值設(shè)置為var4。并且返回true。
如果內(nèi)存值與var5不一致,那么就返回false。
?鎖在java中有兩種實(shí)現(xiàn)方式:synchronized關(guān)鍵字和Lock類。
?synchronized修飾的對(duì)象
??修飾代碼塊,作用于調(diào)用的對(duì)象
synchronized{
}
??修飾方法,作用于調(diào)用的對(duì)象
public synchronized int add(int a,int b){
return a+b;
}
??修飾靜態(tài)方法,作用于所有對(duì)象
public synchronized static int add(int a,int b){
return a+b;
}
??修飾類,作用于所有對(duì)象
synchronized (Test.class){
}
可見性
?當(dāng)一個(gè)線程修改了共享變量的值后,另一個(gè)線程可以立即看到修改后的值。
?共享變量不可見的原因:
??共享變量更新后的值沒有在工作內(nèi)存與主內(nèi)存之間及時(shí)更新。
?內(nèi)存可見性實(shí)現(xiàn)的方式:
??synchronized關(guān)鍵字和volatile關(guān)鍵字
?synchronized關(guān)鍵字內(nèi)存可見性的原因:
??①線程解鎖前,必須把共享變量的最新值刷新到主內(nèi)存中。
??②線程加鎖時(shí),將清空工作內(nèi)存中共享變量的值,從而使用共享變量時(shí),需要從主內(nèi)存中重新讀取最新的值。
?volatile關(guān)鍵字內(nèi)存可見性的原因:通過內(nèi)存屏障和禁止重排序?qū)崿F(xiàn)的
??①volatile會(huì)在寫操作時(shí),會(huì)在寫操作后加一條store屏障指令,將本地內(nèi)存中的共享變量值刷新到主內(nèi)存
??②volatile在進(jìn)行讀操作時(shí),會(huì)在讀操作前加一條load指令,從內(nèi)存中讀取共享變量。