今天是九一八事變爆發 88 週年,國恥日。java
每當我看到祖國的航空航天、互聯網、5G 這些科技方面趕超美國的新聞時,我心裏都異常激動,從 "東亞病夫" 到吾輩自強,靠得是無數中華兒女的實幹。算法
銘記歷史...編程
工做再忙,仍是但願堅持把編程知識分享下去,繼續 Java 併發編程。併發
在併發編程中,常常會遇到多個線程訪問同一個共享變量,當同時對共享變量進行讀寫操做時,就會產生數據不一致的狀況。app
爲了解決這個問題ide
JDK 1.5 以前,使用 synchronized 關鍵字,拿到 Java 對象的鎖,保護鎖定的代碼塊。JVM 保證同一時刻只有一個線程能夠拿到這個 Java 對象的鎖,執行對應的代碼塊。工具
JDK 1.5 開始,引入了併發工具包 java.util.concurrent.locks.Lock,讓鎖的功能更加豐富。性能
synchronized 關鍵字鎖定代碼庫spa
可重入鎖 java.util.concurrent.lock.ReentrantLock線程
可重複讀寫鎖 java.util.concurrent.lock.ReentrantReadWriteLock
可重入鎖
指在同一個線程在外層方法獲取鎖的時候,進入內層方法會自動獲取鎖。JDK 中基本都是可重入鎖,避免死鎖的發生。上面提到的常見的鎖都是可重入鎖。
公平鎖 / 非公平鎖
公平鎖,指多個線程按照申請鎖的順序來獲取鎖。如 java.util.concurrent.lock.ReentrantLock.FairSync
非公平鎖,指多個線程獲取鎖的順序並非按照申請鎖的順序,有可能後申請的線程先得到鎖。如 synchronized、java.util.concurrent.lock.ReentrantLock.NonfairSync
獨享鎖 / 共享鎖
獨享鎖,指鎖一次只能被一個線程所持有。synchronized、java.util.concurrent.locks.ReentrantLock 都是獨享鎖
共享鎖,指鎖可被多個線程所持有。ReadWriteLock 返回的 ReadLock 就是共享鎖
悲觀鎖 / 樂觀鎖
悲觀鎖,一概會對代碼塊進行加鎖,如 synchronized、java.util.concurrent.locks.ReentrantLock
樂觀鎖,默認不會進行併發修改,一般採用 CAS 算法不斷嘗試更新
悲觀鎖適合寫操做較多的場景,樂觀鎖適合讀操做較多的場景
粗粒度鎖 / 細粒度鎖
粗粒度鎖,就是把執行的代碼塊都鎖定
細粒度鎖,就是鎖住儘量小的代碼塊,java.util.concurrent.ConcurrentHashMap 中的分段鎖就是一種細粒度鎖
粗粒度鎖和細粒度鎖是相對的,沒有什麼標準
偏向鎖 / 輕量級鎖 / 重量級鎖
JDK 1.5 以後新增鎖的升級機制,提高性能。
經過 synchronized 加鎖後,一段同步代碼一直被同一個線程所訪問,那麼該線程獲取的就是偏向鎖
偏向鎖被一個其餘線程訪問時,Java 對象的偏向鎖就會升級爲輕量級鎖
再有其餘線程會以自旋的形式嘗試獲取鎖,不會阻塞,自旋必定次數仍然未獲取到鎖,就會膨脹爲重量級鎖
自旋鎖
自旋鎖是指嘗試獲取鎖的線程不會當即阻塞,而是採用循環的方式去嘗試獲取鎖,這樣的好處是減小線程上下文切換的消耗,缺點是循環佔有、浪費 CPU 資源