Synchornized做了哪些優(yōu)化信峻?
synchornized底層都是使用monitorenter和monitorexit指令實現(xiàn)的衩侥,進入同步塊就意味著拿到了monitorenter的所有權(quán)涛舍,而持有這個monitorenter所有權(quán)的線程就可以執(zhí)行代碼塊,沒有持有的線程就都在外面等著锐极,早期JDK1.6之前都是使用阻塞線程的方式等待鎖的釋放盒齿,但很多輕量級的同步塊,迅速執(zhí)行完的代碼塊來說枚荣,這個鎖就太重了碗脊,所以就在重量級鎖的基礎(chǔ)上引入了偏向鎖和輕量級鎖這兩個狀態(tài),如果當前的synchornized加鎖的情況下面橄妆,他這個偏向鎖衙伶,是當前的對象沒有競爭,而且總是由同一個線程獲得鎖的時候就會進入偏向鎖的狀態(tài)害碾,一旦產(chǎn)生了競爭就會膨脹為輕量級鎖矢劲,如果輕量級鎖自旋超過了一定次數(shù)又會膨脹為重量級鎖。
synchronized的執(zhí)行過程:
- 檢測Mark Word里面是不是當前線程的ID慌随,如果是芬沉,表示當前線程處于偏向鎖
- 如果不是,則使用CAS將當前線程的ID替換Mard Word阁猜,如果成功則表示當前線程獲得偏向鎖丸逸,置偏向標志位1
- 如果失敗,則說明發(fā)生競爭蹦漠,撤銷偏向鎖椭员,進而升級為輕量級鎖。
- 當前線程使用CAS將對象頭的Mark Word替換為鎖記錄指針笛园,如果成功隘击,當前線程獲得鎖
- 如果失敗,表示其他線程競爭鎖研铆,當前線程便嘗試使用自旋來獲取鎖埋同。
- 如果自旋成功則依然處于輕量級狀態(tài)。
- 如果自旋失敗棵红,則升級為重量級鎖凶赁。
Monitor 原理
每個 Java 對象都可以關(guān)聯(lián)一個Monitor對象,如果使用 synchronized 給對象上鎖(重量級)之后,該對象頭的
Mark Word 中就被設(shè)置指向Monitor 對象的指針虱肄。
Monitor 結(jié)構(gòu)如下:
剛開始 Monitor 中 Owner 為 null致板。
當 Thread-2 執(zhí)行 synchronized(obj) 就會將 Monitor 的所有者 Owner 置為 Thread-2,Monitor中只能有一個Owner咏窿。
在 Thread-2上鎖的過程中斟或,如果 Thread-3,Thread-4集嵌,Thread-5 也來執(zhí)行 synchronized(obj)萝挤,就會進入
EntryList BLOCKED。
Thread-2 執(zhí)行完同步代碼塊的內(nèi)容根欧,然后喚醒EntryList 中等待的線程來競爭鎖怜珍,競爭的時是非公平的。
圖中 WaitSet 中的 Thread-0凤粗,Thread-1 是之前獲得過鎖酥泛,但條件不滿足進入調(diào)用wait()方法進入 WAITING 狀態(tài)的線程。當調(diào)用notifyAll()方法之后侈沪,WaitSet中的 Thread-0揭璃,Thread-1 進入EntryList BLOCKED。
自適應(yīng)自旋鎖
自旋鎖優(yōu)點在于它避免一些線程的掛起和恢復操作亭罪,因為掛起線程和恢復線程都需要從用戶態(tài)轉(zhuǎn)入內(nèi)核態(tài)瘦馍,這個過程是比較慢的,所以通過自旋的方式可以一定程度上避免線程掛起和恢復所造成的性能開銷
但是应役,如果長時間自旋還獲取不到鎖情组,那么也會造成一定的資源浪費,所以我們通常會給自旋設(shè)置一個固定的值來避免一直自旋的性能開銷
自適應(yīng)自旋鎖是指箩祥,線程自旋的次數(shù)不再是固定的值院崇,而是一個動態(tài)改變的值。簡單來說袍祖,如果線程自旋成功了底瓣,則下次自旋的次數(shù)會增多,如果失敗蕉陋,下次自旋的次數(shù)會減少捐凭。
上面幾種鎖都是JVM自己內(nèi)部實現(xiàn),當我們執(zhí)行synchronized同步塊的時候jvm會根據(jù)啟用的鎖和當前線程的爭用情況凳鬓,決定如何執(zhí)行同步操作茁肠;