樂觀鎖:樂觀鎖假設認為資料一般情況下不會產生併發衝突,所以在資料進行提交更新的時候,才會正 式對資料是否產生併發衝突進行檢測,如果發現併發衝突了,則讓返回使用者錯誤的資訊,讓使用者決定如 何去做。樂觀鎖的效能比較高。
悲觀鎖:總是假設最壞的情況,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會 上鎖,這樣別人想拿這個資料就會阻塞直到它拿到鎖。
悲觀鎖的問題:總是需要競爭鎖,進而導致發生執行緒切換,掛起其他執行緒;所以效能不高。 樂觀鎖的問題:並不總是能處理所有問題,所以會引入一定的系統複雜度。
樂觀鎖的使用場景:
import j**a.util.concurrent.atomic.atomicinteger;
public class happylock
}公平鎖:鎖的獲取順序必須合執行緒方法的先後順序是儲存一致的,就叫公平鎖 優點:執行時順序的,所以結果是可以預期的
非公平鎖:鎖的獲取方式循序和執行緒獲取鎖的順序無關。優點:效能比較高
按之間的方式處理下,執行緒在搶鎖失敗後進入阻塞狀態,放棄 cpu,需要過很久才能再次被排程。但經過測算,實際的生活中,大部分情況下,雖然當前搶鎖失敗,但過不了很久,鎖就會被釋放。基於這個 事實,自旋鎖誕生了。
你可以簡單的認為自旋鎖就是下面的**
只要沒搶到鎖,就死等。
自旋鎖的缺點:
缺點其實非常明顯,就是如果之前的假設(鎖很快會被釋放)沒有滿足,則執行緒其實是光在消耗 cpu 資源,長期在做無用功的。
可重入鎖的字面意思是「可以重新進入的鎖」,即允許同乙個執行緒多次獲取同一把鎖。比如乙個遞迴函式 裡有加鎖操作,遞迴過程中www.cppcns.com這個鎖會阻塞自己嗎?如果不會,那麼這個鎖就是可重入鎖(因為這個原因 可重入鎖也叫做遞迴鎖)。
j**a裡只要以reentrant開頭命名的鎖都是可重入鎖,而且jdk提供的所有現成的lock實現類,包括
synchronized關鍵字鎖都是可重入的。
面試題:
1.你是怎麼理解樂觀鎖和悲觀鎖的,具體怎麼實現呢?
樂觀鎖——> cas ——> atomic.(cas是由v(記憶體值) a(預期值)b(新值))組成,然後執行的時候是使用v=a對比,如果結果為true,這表明沒有併發衝突,則可以直接進行修改,否則返回錯誤資訊。*
2.有了解什麼讀寫鎖麼?
多執行緒之間,資料的讀取方之間不會產生執行緒安全問題,但資料的寫入方互相之間以及和讀者之間都需 要進行互斥。如果兩種場景下都用同乙個鎖,就會產生極大的效能損耗。所以讀寫鎖因此而產生。
讀寫鎖(readers-writer lock),看英文可以顧名思義,在執行加鎖操作時需要額外表明讀寫意圖,複數讀者之間並不互斥,而寫者則要求與任何人互斥。
把鎖分成兩個鎖,乙個是讀鎖,乙個是寫鎖,其中讀鎖可以多個執行緒擁有,而寫鎖是乙個執行緒擁有
3.什麼是自旋鎖,為什麼要使用自旋鎖策略呢,缺點是什麼?
按之間的方式處理下,執行緒在搶鎖失敗後進入阻塞狀態,放棄 cpu,需要過很久才能再次被排程。但經過測算,實際的生活中,大部分情況下,雖然當前搶鎖失敗,但過不了很久,鎖就會被釋放。基於這個 事實,自旋鎖誕生了。
你可以簡單的認為自旋鎖就是下面的**
只要沒搶到鎖,就死等。
自旋鎖的缺點:
缺點其實非常明顯,就是如果之前的假設(鎖很快會被釋放)沒有滿足,則執行緒其實是光在消耗 cpu 資源,長期在做無用功的。
4.synchronized 是可重入鎖麼?
synchronized 是可重入鎖,
**如下:
public class chonglock }}
}cas: 全稱compare and swap,字面意思:」比較並交換「,乙個 cas 涉及到以下操作:
我們假設記憶體中的原資料v,舊的預期值a,需要修改的新值b。 1. 比較 a 與 v 是否相等。(比較) 2. 如果比較相等,將 b 寫入 v。(交換) 3. 返回操作是否成功。
當多個執行緒同時對某個資源進行cas操作,只能有乙個執行緒操作成功,但是並不會阻塞其他執行緒,其他執行緒只會收到操作失敗的訊號。可見 cas 其實是乙個樂觀鎖。
針對不同的作業系統,jvm 用到了不同的 cas 實現原理,簡單來講:
j**a 的 cas 利用的的是 unsafe 這個類提供的 cas 操作;
unsafe 的 cas 依 賴 了 的 是 jvm 針 對 不 同 的 操 作 系 統 實 現 的 atomic::cmpxchg(乙個原子性的指令)
/atomic::cmpxchg 的實現使用了彙編的 cas 操作,並使用 cpu 硬體提供的 lock 機制保證其原子性。
簡而言之,是因為硬體予以了支援,軟體層面才能做到。
2.3.1 實現自旋鎖
public class spinlock
}public void unlock ()
}用於實現原子類
示例**:
public class atomicinteger
}public class unsafe while(!this.compareandswapint(var1, var2, var5, var5 + var4));
return var5;
}}aba 的問題,就是乙個值從a變成了b又變成了a,而這個期間我們不清楚這個過程。
我來舉乙個例子,如果你向別人轉錢,你需要轉100元,但是你點選了兩次轉錢,第一次會成功,但是第二次肯定會失敗,但是,在你點選第二次轉錢的同一時刻,你的公司給你轉了100元工資,那麼你就會莫名其妙的把100又轉了出去,你丟失了100,別人也沒有獲得100.
**演示:
1.正常轉錢流程
import j**a.util.concurrent.atomic.atomicreference;
public class aba
});t1.start();
//轉賬執行緒2
thread t2 = new thread(new runnable()
}});
t2.start();}}
2.錯誤操作後:
import j**a.util.concurrent.atomic.atomicreference;
public class abas
});t1.start();
t1.join();
//轉入100
thread t3 = new thread(new runnable()
});t3.start();
//轉賬執行緒2
t3.join();
thread t2 = new thread(new runnable()
});t2.start();}}
解決aba方法
解決方法:加入版本資訊,例如攜帶 atomicstampedreference 之類的時間戳作為版本資訊,保證不會
出現老的值。
**實現:
import j**a.util.concurrent.atomic.atomicreference;
import j**a.util.concurrent.akhywutzptomic.atomicstampedreference;
public class abaack
});t1.start();
t1.join();
//轉入100
thread t3 = new thread(new runnable()
});t3.start();
//轉賬執行緒2
t3.join();
thread t2 = new thread(new runnable()
});t2.start();
//integer的快取記憶體是-128--127(atomicstampedreference)
//如果大於127,那麼就開始new物件了
/** 解決方法,調整邊界值*/}}
以上就是今天要講的內容,本文僅僅簡單介紹了鎖策略,解決執行緒安全。
程式猿必須掌握的git命令
git status s m readme m表示修改過的檔案,沒有add mm rakefile 第乙個m表示修改過沒有add,第二個m表示 add了但是沒有commit a lib git.rb a表示新新增的檔案,已經add但是還沒有commit m lib git.rb m表示修改過但是沒有...
程式設計師必須要掌握的語言
在有許多程式開發工具可供選擇的今天,強調 c c 是程式設計師所必須掌握的語言 難道就沒有人真的敢站出來提出質疑嗎?下面就由卓躍教育為您做乙個介紹 誠然,我們不能否認c c 語言的超凡魅力。然而我們不禁要設問,在windows流行的今天,用windowsc編制出介面獨特 功能強大的應用程式,你能嗎?...
設計安全的多執行緒應用程式 執行緒安全
以前常聽高手告誡mfc物件不要跨執行緒使用,因為mfc不是執行緒安全的。比如cwnd物件不要跨執行緒使用,可以用視窗控制代碼 hwnd 代替。csocket casyncsocket物件不要跨執行緒使用,用socket控制代碼代替.那麼到底什麼是執行緒安全呢?什麼時候需要考慮?如果程式涉及到多 執行...