




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第java自旋鎖和JVM對鎖的優(yōu)化詳解目錄背景好處AtomicLong的實現(xiàn)getAndIncrement方法實驗缺點適用場景JVM對鎖做了哪些優(yōu)化?自適應的自旋鎖鎖消除鎖粗化偏向鎖/輕量級鎖/重量級鎖鎖升級
背景
先上圖
由此可見,非自旋鎖如果拿不到鎖會把線程阻塞,直到被喚醒;
自旋鎖拿不到鎖會一直嘗試
為什么要這樣?
好處
阻塞和喚醒線程都是需要高昂的開銷的,如果同步代碼塊中的內容不復雜,那么可能轉換線程帶來的開銷比實際業(yè)務代碼執(zhí)行的開銷還要大。
在很多場景下,可能我們的同步代碼塊的內容并不多,所以需要的執(zhí)行時間也很短,如果我們僅僅為了這點時間就去切換線程狀態(tài),那么其實不如讓線程不切換狀態(tài),而是讓它自旋地嘗試獲取鎖,等待其他線程釋放鎖,有時我只需要稍等一下,就可以避免上下文切換等開銷,提高了效率。
用一句話總結自旋鎖的好處,那就是自旋鎖用循環(huán)去不停地嘗試獲取鎖,讓線程始終處于Runnable狀態(tài),節(jié)省了線程狀態(tài)切換帶來的開銷。
AtomicLong的實現(xiàn)
getAndIncrement方法
publicfinallonggetAndIncrement(){
returnunsafe.getAndAddLong(this,valueOffset,1L);
publicfinallonggetAndAddLong(Objecto,longoffset,longdelta){
longv;
do{
v=getLongVolatile(o,offset);
//如果修改過程中遇到其他線程競爭導致沒修改成功,死循環(huán),直到修改成功為止
}while(!compareAndSwapLong(o,offset,v,v+delta));
returnv;
實驗
packagecom.reflect;
importjava.util.concurrent.atomic.AtomicReference;
classReentrantSpinLock{
privateAtomicReferenceThreadowner=newAtomicReference();
privateintcount=0;
publicvoidlock(){
Threadt=Thread.currentThread();
if(t==owner.get()){
++count;
return;
while(!pareAndSet(null,t)){
System.out.println("自旋了");
publicvoidunlock(){
Threadt=Thread.currentThread();
if(t==owner.get()){
if(count0){
--count;
}else{
owner.set(null);
publicstaticvoidmain(String[]args){
ReentrantSpinLockspinLock=newReentrantSpinLock();
Runnablerunnable=newRunnable(){
@Override
publicvoidrun(){
System.out.println(Thread.currentThread().getName()+"開始嘗試獲取自旋鎖");
spinLock.lock();
try{
System.out.println(Thread.currentThread().getName()+"獲取到了自旋鎖");
Thread.sleep(4000);
}catch(InterruptedExceptione){
e.printStackTrace();
}finally{
spinLock.unlock();
System.out.println(Thread.currentThread().getName()+"釋放了了自旋鎖");
Threadthread1=newThread(runnable);
Threadthread2=newThread(runnable);
thread1.start();
thread2.start();
很多自旋了,說明自旋期間CPU依然在不停運轉
缺點
雖然避免了線程切換的開銷,但是在避免線程切換開銷的同時帶來新的開銷:不停嘗試獲取鎖,如果這個鎖一直不能被釋放那么這種嘗試知識無用的嘗試,浪費處理器資源,就是說一開始自旋鎖開銷低于線程切換,但是隨著時間增加,這種開銷后期甚至超過線程切換的開銷,得不償失
適用場景
并發(fā)不是特別高的場景臨界區(qū)比較短小的情況,利用避免線程切換提高效率
如果臨界區(qū)很大,線程拿到鎖很久才釋放,那自旋會一直占用CPU但無法拿到鎖,浪費資源
JVM對鎖做了哪些優(yōu)化?
相比于JDK1.5,在JDK1.6中HotSopt虛擬機對synchronized內置鎖的性能進行了很多優(yōu)化,包括自適應的自旋、鎖消除、鎖粗化、偏向鎖、輕量級鎖等。有了這些優(yōu)化措施后,synchronized鎖的性能得到了大幅提高,下面我們分別介紹這些具體的優(yōu)化。
自適應的自旋鎖
在JDK1.6中引入了自適應的自旋鎖來解決長時間自旋的問題。自適應意味著自旋的時間不再固定,而是會根據最近自旋嘗試的成功率、失敗率,以及當前鎖的擁有者的狀態(tài)等多種因素來共同決定。自旋的持續(xù)時間是變化的,自旋鎖變聰明了。比如,如果最近嘗試自旋獲取某一把鎖成功了,那么下一次可能還會繼續(xù)使用自旋,并且允許自旋更長的時間;但是如果最近自旋獲取某一把鎖失敗了,那么可能會省略掉自旋的過程,以便減少無用的自旋,提高效率。
鎖消除
publicclassPerson{
privateStringname;
privateintage;
publicPerson(StringpersonName,intpersonAge){
name=personName;
age=personAge;
publicPerson(Personp){
this(p.getName(),p.getAge());
publicStringgetName(){
returnname;
publicintgetAge(){
returnage;
classEmployee{
privatePersonperson;
publicPersongetPerson(){
returnnewPerson(person);
publicvoidprintEmployeeDetail(Employeeemp){
Personperson=emp.getPerson();
System.out.println("Employee'sname:"+person.getName()+";age:"+person.getAge());
在這段代碼中,我們看到下方的Employee類中的getPerson()方法,這個方法中使用了類里面的person對象,并且新建一個和它屬性完全相同的新的person對象,目的是防止方法調用者修改原來的person對象。但是在這個例子中,其實是沒有任何必要新建對象的,因為我們的printEmployeeDetail()方法沒有對這個對象做出任何的修改,僅僅是打印,既然如此,我們其實可以直接打印最開始的person對象,而無須新建一個新的。
如果編譯器可以確定最開始的person對象不會被修改的話,它可能會優(yōu)化并且消除這個新建person的過程。根據這樣的思想,接下來我們就來舉一個鎖消除的例子,,經過逃逸分析之后,如果發(fā)現(xiàn)某些對象不可能被其他線程訪問到,那么就可以把它們當成棧上數據,棧上數據由于只有本線程可以訪問,自然是線程安全的,也就無需加鎖,所以會把這樣的鎖給自動去除掉。
例如,我們的StringBuffffer的append方法如下所示:
@Override
publicsynchronizedStringBufferappend(Objectobj){
toStringCache=null;
super.append(String.valueOf(obj));
returnthis;
從代碼中可以看出,這個方法是被synchronized修飾的同步方法,因為它可能會被多個線程同時使用。
但是在大多數情況下,它只會在一個線程內被使用,如果編譯器能確定這個StringBuffffer對象只會在一個線程內被使用,就代表肯定是線程安全的,那么我們的編譯器便會做出優(yōu)化,把對應的synchronized給消除,省去加鎖和解鎖的操作,以便增加整體的效率。
鎖粗化
釋放了鎖,緊接著什么都沒做,又重新獲取鎖
publicvoidlockCoarsening(){
synchronized(this){
synchronized(this){
synchronized(this){
那么其實這種釋放和重新獲取鎖是完全沒有必要的,如果我們把同步區(qū)域擴大,也就是只在最開始加一次鎖,并且在最后直接解鎖,那么就可以把中間這些無意義的解鎖和加鎖的過程消除,相當于是把幾個synchronized塊合并為一個較大的同步塊。這樣做的好處在于在線程執(zhí)行這些代碼時,就無須頻繁申請與釋放鎖了,這樣就減少了性能開銷。
不過,我們這樣做也有一個副作用,那就是我們會讓同步區(qū)域變大。如果在循環(huán)中我們也這樣做,如代碼所示:
for(inti=0;i1000;i++){
synchronized(this){
也就是我們在第一次循環(huán)的開始,就開始擴大同步區(qū)域并持有鎖,直到最后一次循環(huán)結束,才結束同步代碼塊釋放鎖的話,這就會導致其他線程長時間無法獲得鎖。所以,這里的鎖粗化不適用于循環(huán)的場景,僅適用于非循環(huán)的場景。
鎖粗化功能是默認打開的,用-XX:-EliminateLocks可以關閉該功能
偏向鎖/輕量級鎖/重量級鎖
這三種鎖是特指synchronized鎖的狀態(tài),通過在對象頭中的markword來表明鎖的狀態(tài)
對于偏向鎖而言,它的思想是如果自始至終,對于這把鎖都不存在競爭,那么其實就沒必要上鎖,只要打個標記就行了。一個對象在被初始化后,如果還沒有任何線程來獲取它的鎖時,它就是可偏向的,當有第一個線程來訪問它嘗試獲取鎖的時候,它就記錄下來這個線程,如果后面嘗試獲取鎖的線程正是這個偏向鎖的擁有者,就可以直接獲取鎖,開銷很小。
JVM的開發(fā)者發(fā)現(xiàn)在很多情況下,synchronized中的代碼塊是被多個線程交替執(zhí)行的,也就是說,并不存在實際的競爭,或者是只有短時間的鎖競爭,用CAS就可以解決。這種情況下,重量級鎖是沒必要的。輕量級鎖指當鎖原來是偏向鎖的時候,被另一個線程所訪問,說明存在競爭,那么偏向鎖就會升級為輕量級鎖,線程會通過自旋的方式嘗試獲取鎖,不會阻塞
這種鎖利用操作系統(tǒng)的同步機制實現(xiàn),所以開銷比較大。當多個線程直接有實際競爭,并且鎖競爭
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 投資車行合同協(xié)議書
- 合同變更協(xié)議書范文
- 環(huán)保咨詢合同協(xié)議書
- 合同協(xié)議書紙質
- 口罩合同協(xié)議書
- 增加合同協(xié)議書
- 委托設備購銷合同協(xié)議書
- 合同協(xié)議書字體
- 石材晶面合同協(xié)議書
- 品牌服務協(xié)議書合同范本
- 個人拍攝合同范本
- 第10課和諧校園共同維護(課件)-【中職專用】高一思想政治《心理健康與職業(yè)生涯》(高教版2023·基礎模塊)
- 2024年04月江蘇省農業(yè)農村廳直屬事業(yè)單位2024年公開招考工作人員筆試歷年高頻考題摘選后附答案詳解
- 多圖中華民族共同體概論課件第十三講先鋒隊與中華民族獨立解放(1919-1949)根據高等教育出版社教材制作
- 傷口造口進修匯報護理
- 口才與演講實訓教程智慧樹知到期末考試答案2024年
- 檔案整理及數字化服務售后服務方案
- 打造消費天堂(百貨公司與近代城市文化)
- 2024年一年級《體育與健康》全冊教案
- 臂叢神經損傷康復護理
- 2023路用熱拌環(huán)氧瀝青
評論
0/150
提交評論