簡單了解synchronized和lock的區(qū)別
這篇文章主要介紹了簡單了解synchronized和lock的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
類別 | synchronized | Lock |
---|---|---|
存在層次 | Java的關鍵字 | 一個類 |
鎖的釋放 | 1.以獲取鎖的線程執(zhí)行代碼同步代碼,釋放鎖。 2,線程執(zhí)行發(fā)生異常,jvm會讓線程釋放鎖 |
在finally中必須釋放鎖,不然容易造成線程死鎖 |
鎖的獲取 | 假設A線程獲得鎖,B線程等待,如果A線程阻塞,則B會一直等 | 分情況而定,Lock有多個獲取鎖的方式,線程不用一直等 |
鎖狀態(tài) | 無法判斷 | 可判斷 |
鎖類型 | 可重入,不可中斷,非公平 | 可重入,可判斷,可公平 |
性能 | 少量同步 | 大量同步 |
1.Lock
Lock的幾個方法
- lock() 獲取鎖
- unlock() 釋放鎖
- trylock() 獲得鎖的狀態(tài),返回true和false
- tryLock(long time,TimeUnit unit) 比tryLock加了時間期限
- lockInterruptibly()
2. synchronized
3.樂觀鎖和悲觀鎖
樂觀鎖( Optimistic Locking )
相對悲觀鎖而言,樂觀鎖機制采取了更加寬松的加鎖機制。悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大程度的獨占性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。而樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基于數據版本( Version )記錄機制實現。
何謂數據版本?即為數據增加一個版本標識,在基于數據庫表的版本解決方案中,一般是通過為數據庫表增加一個 “version” 字段來實現。讀取出數據時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大于數據庫表當前版本號,則予以更新,否則認為是過期數據。
悲觀鎖(Pessimistic Lock)
正如其名,具有強烈的獨占和排他特性。它指的是對數據被外界(包括本系統(tǒng)當前的其他事務,以及來自外部系統(tǒng)的事務處理)修改持保守態(tài)度,因此,在整個數據處理過程中,將數據處于鎖定狀態(tài)。悲觀鎖的實現,往往依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統(tǒng)中實現了加鎖機制,也無法保證外部系統(tǒng)不會修改數據)。
悲觀鎖(適合多寫場景)
總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完后再把資源轉讓給其它線程)。傳統(tǒng)的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中synchronized和ReentrantLock等獨占鎖就是悲觀鎖思想的實現
樂觀鎖(適合多讀場景)
總是假設最好的情況,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現。樂觀鎖適用于多讀的應用類型,這樣可以提高吞吐量,像數據庫提供的類似于write_condition機制,其實都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的。(compare and swap)
樂觀鎖的缺點
- ABA問題
- 循環(huán)時間長開銷大
- 只能保證一個共享變量的原子操作
4.Java線程阻塞的代價
java的線程是映射到操作系統(tǒng)原生線程之上的,如果要阻塞或喚醒一個線程就需要操作系統(tǒng)介入,需要在戶態(tài)與核心態(tài)之間切換,這種切換會消耗大量的系統(tǒng)資源,因為用戶態(tài)與內核態(tài)都有各自專用的內存空間,專用的寄存器等,用戶態(tài)切換至內核態(tài)需要傳遞給許多變量、參數給內核,內核也需要保護好用戶態(tài)在切換時的一些寄存器值、變量等,以便內核態(tài)調用結束后切換回用戶態(tài)繼續(xù)工作。
如果線程狀態(tài)切換是一個高頻操作時,這將會消耗很多CPU處理時間;
如果對于那些需要同步的簡單的代碼塊,獲取鎖掛起操作消耗的時間比用戶代碼執(zhí)行的時間還要長,這種同步策略顯然非常糟糕的。
synchronized會導致爭用不到鎖的線程進入阻塞狀態(tài),所以說它是java語言中一個重量級的同步操縱,被稱為重量級鎖,為了緩解上述性能問題,JVM從1.5開始,引入了輕量鎖與偏向鎖,默認啟用了自旋鎖,他們都屬于樂觀鎖。
5.重量級鎖和輕量級鎖
1. 自旋鎖
自旋鎖原理非常簡單,如果持有鎖的線程能在很短時間內釋放鎖資源,那么那些等待競爭鎖的線程就不需要做內核態(tài)和用戶態(tài)之間的切換進入阻塞掛起狀態(tài),它們只需要等一等(自旋),等持有鎖的線程釋放鎖后即可立即獲取鎖,這樣就避免用戶線程和內核的切換的消耗。
但是線程自旋是需要消耗cup的,說白了就是讓cup在做無用功,如果一直獲取不到鎖,那線程也不能一直占用cup自旋做無用功,所以需要設定一個自旋等待的最大時間。
如果持有鎖的線程執(zhí)行的時間超過自旋等待的最大時間扔沒有釋放鎖,就會導致其它爭用鎖的線程在最大等待時間內還是獲取不到鎖,這時爭用線程會停止自旋進入阻塞狀態(tài)。
自旋鎖的優(yōu)缺點
自旋鎖盡可能的減少線程的阻塞,這對于鎖的競爭不激烈,且占用鎖時間非常短的代碼塊來說性能能大幅度的提升,因為自旋的消耗會小于線程阻塞掛起再喚醒的操作的消耗,這些操作會導致線程發(fā)生兩次上下文切換!
但是如果鎖的競爭激烈,或者持有鎖的線程需要長時間占用鎖執(zhí)行同步塊,這時候就不適合使用自旋鎖了,因為自旋鎖在獲取鎖前一直都是占用cpu做無用功,占著XX不XX,同時有大量線程在競爭一個鎖,會導致獲取鎖的時間很長,線程自旋的消耗大于線程阻塞掛起操作的消耗,其它需要cup的線程又不能獲取到cpu,造成cpu的浪費。所以這種情況下我們要關閉自旋鎖;
自旋鎖時間閾值
自旋鎖的目的是為了占著CPU的資源不釋放,等到獲取到鎖立即進行處理。但是如何去選擇自旋的執(zhí)行時間呢?如果自旋執(zhí)行時間太長,會有大量的線程處于自旋狀態(tài)占用CPU資源,進而會影響整體系統(tǒng)的性能。因此自旋的周期選的額外重要!
JVM對于自旋周期的選擇,jdk1.5這個限度是一定的寫死的,在1.6引入了適應性自旋鎖,適應性自旋鎖意味著自旋的時間不在是固定的了,而是由前一次在同一個鎖上的自旋時間以及鎖的擁有者的狀態(tài)來決定,基本認為一個線程上下文切換的時間是最佳的一個時間,同時JVM還針對當前CPU的負荷情況做了較多的優(yōu)化
- 如果平均負載小于CPUs則一直自旋
- 如果有超過(CPUs/2)個線程正在自旋,則后來線程直接阻塞
- 如果正在自旋的線程發(fā)現Owner發(fā)生了變化則延遲自旋時間(自旋計數)或進入阻塞
- 如果CPU處于節(jié)電模式則停止自旋
- 自旋時間的最壞情況是CPU的存儲延遲(CPU A存儲了一個數據,到CPU B得知這個數據直接的時間差)
- 自旋時會適當放棄線程優(yōu)先級之間的差異
自旋鎖的開啟
JDK1.7后,去掉此參數,由jvm控制
2.重量級鎖(synchronized)
synchronized的作用
- 作用與方法時,鎖住的是對象的實例
- 當作用于靜態(tài)方法時,鎖住的是Class實例,又因為Class的相關數據存儲在永久帶PermGen(jdk1.8則metaspace),永久帶是全局共享的,因此靜態(tài)方法鎖相當于類的一個全局鎖,會鎖所有調用該方法的線程
- synchronized作用于一個對象實例時,鎖住的是所有以該對象為鎖的代碼塊
synchronized的實現
3.偏向鎖
Java偏向鎖(Biased Locking)是Java6引入的一項多線程優(yōu)化。
偏向鎖,顧名思義,它會偏向于第一個訪問鎖的線程,如果在運行過程中,同步鎖只有一個線程訪問,不存在多線程爭用的情況,則線程是不需要觸發(fā)同步的,這種情況下,就會給線程加一個偏向鎖。
如果在運行過程中,遇到了其他線程搶占鎖,則持有偏向鎖的線程會被掛起,JVM會消除它身上的偏向鎖,將鎖恢復到標準的輕量級鎖。
4.輕量級鎖
輕量級鎖是由偏向所升級來的,偏向鎖運行在一個線程進入同步塊的情況下,當第二個線程加入鎖爭用的時候,偏向鎖就會升級為輕量級鎖;
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- 深入Synchronized和java.util.concurrent.locks.Lock的區(qū)別詳解
- 詳談Lock與synchronized 的區(qū)別
- Java編程synchronized與lock的區(qū)別【推薦】
- 淺談Synchronized和Lock的區(qū)別
- 通過實例解析synchronized和lock區(qū)別
- Java 多線程Synchronized和Lock的區(qū)別
- 淺談Java中Lock和Synchronized的區(qū)別
- Java常用鎖synchronized和ReentrantLock的區(qū)別
- synchronized?和?Lock?的異同點(如何讓選擇)
相關文章
SpringBoot程序打包失敗(.jar中沒有主清單屬性)
在學習SpringBoot,打包SpringBoot程序后,在cmd運行出現了 某某某.jar中沒有注清單屬性,本文就來介紹一下原因以及解決方法,感興趣的可以了解一下2023-06-06Spring實戰(zhàn)之讓Bean獲取Spring容器操作示例
這篇文章主要介紹了Spring實戰(zhàn)之讓Bean獲取Spring容器操作,結合實例形式分析了Bean獲取Spring容器的相關原理、實現方法及操作注意事項,需要的朋友可以參考下2019-11-11一文搞懂JMeter engine中HashTree的配置問題
本文主要介紹了JMeter engine中HashTree的配置,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09