在並發編程中,經常會遇到多個線程訪問同一個共享變量,當同時對共享變量進行讀寫操作時,就會產生數據不一致的情況。
為了解決這個問題
- JDK 1.5 之前,使用 synchronized 關鍵字,拿到 Java 對象的鎖,保護鎖定的代碼塊。JVM 保證同一時刻只有一個線程可以拿到這個 Java 對象的鎖,執行對應的代碼塊。
- JDK 1.5 開始,引入了並發工具包 java.util.concurrent.locks.Lock,讓鎖的功能更加豐富。
常見的鎖
- synchronized 關鍵字鎖定代碼庫
- 可重入鎖 java.util.concurrent.lock.ReentrantLock
- 可重復讀寫鎖 java.util.concurrent.lock.ReentrantReadWriteLock
Java 中不同維度的鎖分類
可重入鎖
- 指在同一個線程在外層方法獲取鎖的時候,進入內層方法會自動獲取鎖。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 資源
- Java 自學指南
- Java 面試題匯總PC端瀏覽【點這里】
- Java知識圖譜
- Java 面試題匯總小程序瀏覽,掃二維碼
所有資源資源匯總於公眾號