volatile關(guān)鍵字
定義:對(duì)該變量禁止使用CPU緩存搜贤,而從主內(nèi)存中讀寫
特性:
- 禁止編碼優(yōu)化(禁止指令重排序)
- 保證變量的線程可見(jiàn)性,即線程B對(duì)線程A的操作是可見(jiàn)的钝凶,即原則1 遵循h(huán)appens-before原則
- 不會(huì)對(duì)線程阻塞仪芒,而只是對(duì)變量的"讀或?qū)?保證原子性,但不對(duì)"讀并且寫"保證原子性耕陷〉嗝可以理解為有兩個(gè)鎖:讀鎖和寫鎖,但不可同時(shí)讀和寫哟沫,見(jiàn)increase方法饺蔑;故此時(shí)一寫多讀時(shí)可以保證數(shù)據(jù)一致。
若要多寫多讀嗜诀,synchronize關(guān)鍵字或Lock類
volatile static int inc = 0;
public static void main(String[] args) {
Runnable runnable = ()->{
increase();
};
ExecutorService executorService = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
executorService.execute(runnable);
}
// 等待所有線程結(jié)束
executorService.shutdown();
System.out.println(inc);
}
private static /*synchronized*/ void increase() {
for (int i = 0; i < 1000; i++) {
// 如線程A執(zhí)行至256次內(nèi)存讀到256猾警,線程B已經(jīng)執(zhí)行完1000次寫入內(nèi)存1000,此時(shí)線程A第257次內(nèi)存讀到1000隆敢,繼續(xù)剩下的743次循環(huán)
inc++; // inc = inc + 1 讀inc并且寫inc
}
}
19418
Process finished with exit code 0
happens-befores原則:
定義:前一個(gè)操作的結(jié)果對(duì)后續(xù)操作是可見(jiàn)的
共8條原則
主要為
- 順序性
- volatile原則
- 傳遞性
...
見(jiàn)Java并發(fā)編程實(shí)戰(zhàn)
java 8大happen-before原則超全面詳解
synchronize關(guān)鍵字
定義:Java中互斥鎖技術(shù)的實(shí)現(xiàn)
特性:
- 可修飾方法发皿,代碼塊
class X {
// 修飾非靜態(tài)方法
synchronized void foo() {
// 臨界區(qū)
}
// 修飾靜態(tài)方法
synchronized static void bar() {
// 臨界區(qū)
}
// 修飾代碼塊
Object obj = new Object();
void baz() {
synchronized(obj) {// lock()
// 臨界區(qū)
//unlock()
}
}
}
- 修飾static方法時(shí)拂蝎,實(shí)際鎖的是該類的.class對(duì)象穴墅;修飾非static方法時(shí),鎖的時(shí)該this對(duì)象
注意:使用鎖synchronized要注意以哪個(gè)對(duì)象為鎖温自,和要保護(hù)的資源(臨界區(qū))封救,在同一個(gè)鎖下的臨界區(qū)是保證原子性的
class obj {
synchronized(obj.class) static void foo1(){}
// obj.class是單例的
synchronized(this) void foo2(){}
// this是可以new出多個(gè)的
}
- wait()、notify()捣作、notifyAll()只能在sychronized代碼塊中使用
sychronized(this){
this.wait() // 此處釋放的鎖一定為this誉结,即鎖對(duì)象
// 若鎖對(duì)象為targer,則為target.wait()
}
wait():釋放該互斥鎖券躁,同時(shí)該線程進(jìn)入等待隊(duì)列惩坑,使其他線程可以搶占該鎖 sleep()使線程阻塞,不會(huì)釋放鎖
notify(): 隨機(jī)通知等待隊(duì)列中的一個(gè)線程也拜,條件滿足以舒,可以執(zhí)行wait()之后代碼
使用notifyAll():通知隊(duì)列中所有線程,條件滿足
Lock接口
定義:Lock為concurrent.locks包下的一個(gè)接口慢哈,該locks包提供對(duì)線程鎖操作的方法蔓钟。
特性:
1.常用實(shí)現(xiàn)類:
ReentrantLock lock = new ReentrantLock(); // 實(shí)現(xiàn)Lock接口
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); // 實(shí)現(xiàn)ReadWriteLock,提供讀鎖或?qū)戞i
readWriteLock.writeLock().lock(); // 讀鎖
2.常用方法:
lock.lock();// 獲得鎖
try{
//處理任務(wù)
}catch(Exception ex){
}finally{
lock.unlock(); // 釋放鎖
}
lock.tryLock(); // 嘗試獲得鎖,得到true卵贱,得不到false滥沫,立即返回
悲觀鎖與樂(lè)觀鎖CAS機(jī)制
悲觀鎖
定義:總是假設(shè)最壞的情況侣集,假設(shè)每次取數(shù)據(jù)后都認(rèn)為數(shù)據(jù)會(huì)被其他線程修改,需要保證數(shù)據(jù)的強(qiáng)一致性兰绣。如sychronized關(guān)鍵字和ReentrantLock類世分,都是悲觀鎖。
樂(lè)觀鎖與CAS
定義:假設(shè)每次取得數(shù)據(jù)后缀辩,數(shù)據(jù)不會(huì)被其他線程修改臭埋。
CAS:全稱compareAndSwap,望文生義臀玄,即比較與替換瓢阴。
每次進(jìn)行對(duì)數(shù)據(jù)寫操作時(shí)進(jìn)行一次CAS:
compare:比較工作內(nèi)存中的值A(chǔ)1,是否與主內(nèi)存中地址V中的值A(chǔ)2一致健无;
swap:若A1與A2一致荣恐,則修改地址V中的A2為B;若A1與A2不一致睬涧,則重新讀取地址V中的值,再進(jìn)行任務(wù)處理旗唁,稱為回旋畦浓,該情況可能一直循環(huán)直到一致為止。
CAS下帶來(lái)的ABA問(wèn)題:
按時(shí)間順序有以下任務(wù):
線程1任務(wù):讀地址V检疫,A值修改為B值
線程2任務(wù):讀地址V讶请,A值修改為B值
線程3任務(wù):讀地址V,B值修改為A值
根據(jù)CAS原則屎媳,線程1執(zhí)行后夺溢,線程2回旋,線程3執(zhí)行烛谊,最終值為A
若此時(shí)線程2阻塞风响,則只執(zhí)行1和3,線程2釋放丹禀,根據(jù)CAS原則状勤,執(zhí)行完1和3后,線程2回旋判定A值一致双泪,修改為B持搜,最終值為B
解決方法:每次寫數(shù)據(jù)時(shí),加入版本號(hào)焙矛,判斷A1與A2一致時(shí)葫盼,同時(shí)判斷版本號(hào)是否一致
如concurrent包下的Atomic類,為樂(lè)觀鎖村斟。