成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

京東二面:Java 中一共有 N 種實現鎖的方式,你知道都有哪些嗎?

開發 前端
樂觀讀不能保證讀到的數據是最新的,所以當把數據讀取到局部變量的時候需要通過 lock.validate 方法來校驗是否被修改過,如果是改過了那么就加上悲觀讀鎖,再重新讀取數據到局部變量。

首先,我們先來看下線程安全性的定義,為什么需要鎖?

線程安全,即在多線程編程中,一個程序或者代碼段在并發訪問時,能夠正確地保持其預期的行為和狀態,而不會出現意外的錯誤或者不一致的結果。

而解決線程安全問題,主要分為兩大類:1、無鎖;2、有鎖。

無鎖的方式有:

  1. 局部變量;
  2. 對象加 final 為不可變對象;
  3. 使用 ThreadLocal 作為線程副本對象;
  4. CAS,Compare-And-Swap 即比較并交換,是 Java 十分常見的無鎖實現方式。

小白:那有鎖的方式呢,怎么通過加鎖保證線程安全呢?

別急哈,下面聽我給你一一道來。

Java 有哪些鎖?

從加鎖的策略看,分為隱式鎖和顯示鎖。隱式鎖通過 Synchronized 實現,顯示鎖通過 Lock 實現。

  • 樂觀鎖:顧名思義,它是一種基于樂觀的思想,認為讀取的數據一般不會沖突,不會對其加鎖,而是在最后提交數據更新時判斷數據是否被更新,如果沖突,則更新不成功。
  • 悲觀鎖:它總是假設最壞的情況,每次讀取數據都認為別人會更新,所以每次讀取數據的時候都會加鎖,這樣別人就得阻塞等待它處理完釋放鎖后才能去讀取。

樂觀鎖實現:CAS,比較并交換,通常指的是這樣一種原子操作:針對一個變量,首先比較它的內存值與某個期望值是否相同,如果相同,就給它賦一個新值。

但是,這一篇我們主要來看下悲觀鎖的一些常用實現。

syncroized 是什么?

syncronized 是 Java 中的一個關鍵字,用于控制對共享資源的并發訪問,從而防止多個線程同時訪問某個特定資源,這被稱為同步。這個關鍵字可以用來修飾方法或代碼塊。

syncronized 使用對象鎖保證臨界區內代碼的原子性

圖片圖片

小白:synchronized 的底層原理是什么呀,怎么自己就完成加鎖釋放鎖操作了?

其實 synchronized 的原理也不難,主要有以下兩個關鍵點。

  • synchronized 又被稱為監視器鎖,基于 Monitor 機制實現的,主要依賴底層操作系統的互斥原語 Mutex(互斥量)。Monitor 類比加了鎖的房間,一次只能有一個線程進入,進入房間即持有 Monitor,退出后就釋放 Monitor。
  • 另一個關鍵點是 Java 對象頭,在 JVM 虛擬機中,對象在內存中的存儲結構有三部分:對象頭;實例數據;對齊填充。

對象頭主要包括標記字段 Mark World,元數據指針,如果是數組對象的話,對象頭還必須存儲數組長度。

圖片圖片

synchronized 也是基于此,通過鎖對象的 monitor 獲取和 monitor 釋放來實現,對象頭標記為存儲具體鎖狀態,ThreadId 記錄持有偏向鎖的線程 ID。

這里,又引申另外出一個問題:你知道什么是偏向鎖呢?

小白:不知道,啥玩意?

synchronized 鎖升級過程

說到這里,那就不得不提及 synchronized 的鎖升級機制了,因為 synchronized 的加鎖釋放鎖操作會使得 CPU 在內核態和戶態之間發生切換,有一定性能開銷。在 JDK1.5 版本以后,對 synchronized 做了鎖升級的優化,主要利用輕量級鎖、偏向鎖、自適應鎖等減少鎖操作帶來的開銷,對其性能做了很大提升。

圖片圖片

  1. 無鎖:沒有對資源進行加鎖
  2. 偏向鎖:在大部分情況下,只有一個線程訪問修改資源,該線程自動獲取鎖,降低了鎖操作的代價,這里就通過對象頭的 ThreadId 記錄線程 ID。
  3. 輕量級鎖:當前持有偏向鎖,當有另外的線程來訪問后,偏向鎖會升級為輕量級鎖,別的線程通過自旋形式嘗試獲取鎖,不會阻塞,以提高性能。
  4. 重量級鎖:在自旋次數或時間超過一定閾值時,最后會升級為重量級鎖。

小白:哦哦原來如此,那剛剛你說了 Java 除了隱式鎖之外,還有顯示鎖呢?

ReentrantLock 簡介

在 Java 中,除了對象鎖,還有顯示的加鎖的方式,比如 Lock 接口,用得比較多的就是 ReentrantLock。它的特性如下:

圖片圖片

下面我們再來對比看下 ReentrantLock 和 synchronized 的區別

圖片圖片

從這些對比就能看出 ReentrantLock 使用更加的靈活,特性更加豐富。

ReentrantLock 是一個悲觀鎖,即是同一個時刻,只允許一個線程訪問代碼塊,這一點 synchronized 其實也一樣。

圖片圖片

小白:這個是挺好用的,但是我們有一些讀多寫少的場景中比如緩存,大部分時間都是讀操作,這里每個操作都要加鎖,讀性能不是很差嗎,有沒有更好的方案實現這種場景呀?

當然有的,比如 ReentrantReadWriteLock,讀寫鎖。

ReentrantReadWriteLock 介紹

針對上述場景,Java 提供了讀寫鎖 ReentrantReadWriteLock,它的內部維護了一對相關的鎖,一個用于只讀操作,稱為讀鎖;一個用于寫入操作,稱為寫鎖。

/** Inner class providing readlock */
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock */
    private final ReentrantReadWriteLock.WriteLock writerLock;
    /** Performs all synchronization mechanics */
    final Sync sync;

使用核心代碼如下:

public class LocalCacheService {

    static Map<String, Object> localCache = new HashMap<>();
    static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    static Lock readL = lock.readLock();
    static Lock writeL = lock.writeLock();

    public static Object read(String key) {
        readL.lock();
        try {
            return localCache.get(key);
        } finally {
            readL.unlock();
        }
    }

    public static Object save(String key, String value) {
        writeL.lock();
        try {
            return localCache.put(key, value);
        } finally {
            writeL.unlock();
        }
    }
}

在 ReentrantReadWriteLock 中,多個線程可以同時讀取一個共享資源。

當有其他線程的寫鎖時,讀線程會被阻塞,反之一樣。

圖片圖片

讀寫鎖設計思路

這里有一個關鍵點,就是在 ReentrantLock 中,使用 AQS 的 state 表示同步狀態,表示鎖被一個線程重復獲取的次數。但是在讀寫鎖 ReentrantReadWriteLock 中,如何用一個變量維護這兩個狀態呢?

實際 ReentrantReadWriteLock 采用“高低位切割”的方式來維護,將 state 切分為兩部分:高 16 位表示讀;低 16 位表示寫。

分割之后,通過位運算,假設當前狀態為 S,那么:

  • 寫狀態=S&0x0000FFFF(將高 16 位全部移除),當寫狀態需要加 1,S+1 再運算即可。
  • 讀狀態=S>>>16(無符號補 0 右移 16 位),當讀狀態需要加 1,計算 S+(1<<16)。

圖片圖片

這時,我們再來思考下,如果有線程正在讀,寫線程需要等待讀線程釋放鎖才能獲取鎖,也就是讀的時候不允許寫,那么有沒有更好的方式改進呢?

小白:emm,這個真的難倒我了。。。。。。

什么是 StampedLock?

哈哈莫慌,Java8 已經引入了新的讀寫鎖,StampedLock。它和 ReentrantReadWriteLock 相比,區別在于讀過程允許獲取寫鎖寫入,在原來讀寫鎖的基礎上加了一種樂觀鎖機制,該模式不會阻塞寫鎖,只是最后會對比原來的值,有著更高的并發性能。

StampedLock 三種模式如下:

  • 獨占鎖:和 ReentrantReadWriteLock 一樣,同一時刻只能有一個寫線程獲取資源

圖片圖片

  • 悲觀讀鎖:允許多個線程獲取讀鎖,但是讀寫互斥。

圖片圖片

  • 樂觀讀:沒有加鎖,允許多個線程獲取樂觀讀和讀鎖,同時允許一個寫線程獲取寫鎖。

圖片圖片

小白:那這里可以允許多個讀操作和也給寫線程同時進入共享資源操作,那讀取的數據被改了怎么辦啊??

別擔心,樂觀讀不能保證讀到的數據是最新的,所以當把數據讀取到局部變量的時候需要通過 lock.validate 方法來校驗是否被修改過,如果是改過了那么就加上悲觀讀鎖,再重新讀取數據到局部變量。

責任編輯:武曉燕 來源: 碼哥跳動
相關推薦

2025-03-26 00:35:25

2024-04-19 08:05:26

鎖升級Java虛擬機

2025-01-21 10:04:40

Java并發阻塞隊列

2021-08-05 07:28:25

Java實現方式

2022-07-05 08:05:00

策略模式接口實現類

2017-06-05 18:27:41

黑科技618京東

2022-08-05 08:27:05

分布式系統線程并發

2022-04-29 13:40:55

前端測試后端

2018-12-14 12:07:53

Nginxweb服務器

2022-01-19 13:57:22

ymlSpringSnakeYml

2021-01-26 01:55:24

HTTPS網絡協議加密

2021-03-15 11:20:46

HTTPS優化前端

2023-12-27 08:36:27

2021-08-31 09:55:57

服務開發K8S

2021-12-06 08:03:53

IP地址程序

2024-09-29 08:21:11

2016-02-18 16:40:29

SaaS虛擬化自動化

2024-02-05 12:08:07

線程方式管理

2021-09-10 06:50:03

內容CDN網絡

2014-08-22 10:14:27

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 97成人在线| 亚洲91视频 | 欧美最猛性xxxxx亚洲精品 | 国产亚洲精品久久午夜玫瑰园 | 亚洲视频1区 | 成人h视频 | 国产成人av在线 | 亚洲精品99 | 免费在线国产视频 | 国产精品自产拍 | 国产精品成人久久久久 | 国产一区二区三区久久 | 欧美国产亚洲一区二区 | 国产欧美一区二区三区国产幕精品 | 欧美中文字幕一区二区三区 | 欧美日韩国产一区二区 | 国产乱xxav| 在线观看视频中文字幕 | 久久国产精品亚洲 | 日韩一区二区三区在线观看 | 午夜精品久久 | 日韩电影一区 | 欧美黑人又粗大 | 国精产品一区二区三区 | 欧美日产国产成人免费图片 | 中文字幕亚洲专区 | 久久久久国产视频 | 欧美精品一区二区免费视频 | av资源中文在线天堂 | 欧美色a v| 日韩福利片 | 午夜免费 | 二区av| 欧美一区二区三区在线 | 国产精品美女一区二区三区 | 久久99视频 | 成人欧美一区二区三区黑人孕妇 | 色久影院 | 黄色av免费网站 | 国产精品综合色区在线观看 | 亚洲成人中文字幕 |