最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java與Mysql鎖相關(guān)知識總結(jié)

 更新時(shí)間:2023年04月18日 08:30:20   作者:IntoTw  
這篇文章主要介紹了Java與Mysql鎖相關(guān)知識總結(jié)的相關(guān)資料,需要的朋友可以參考下

鎖的定義

在計(jì)算機(jī)程序中鎖用于獨(dú)占資源,獲取到鎖才可以操作對應(yīng)的資源。

鎖的實(shí)現(xiàn)

鎖在計(jì)算機(jī)底層的實(shí)現(xiàn),依賴于CPU提供的CAS指令(compare and swsp),對于一個(gè)內(nèi)存地址,會比較原值以及嘗試去修改的值,通過值是否修改成功,來表示是否強(qiáng)占到了這個(gè)鎖。

JVM中的鎖

jvm中,有2個(gè)常用的鎖

synchronized

synchronized是java提供的關(guān)鍵字鎖,可以鎖對象,類,方法。
在JDK1.6以后,對synchronized進(jìn)行了優(yōu)化,增加了偏向鎖和輕量鎖模式,現(xiàn)在synchronized鎖的運(yùn)行邏輯如下:

  • 在初始加鎖時(shí),會增加偏向鎖,即“偏向上一次獲取該鎖的線程”,在偏向鎖下,會直接CAS獲取該鎖。該模式大大提高了單線程反復(fù)獲取同一個(gè)鎖的吞吐情況,在Java官方看來,大部分鎖的爭搶都發(fā)生在同個(gè)線程上。
  • 如果偏向鎖CAS獲取失敗,說明當(dāng)前線程與偏向鎖偏向的線程不同,偏向鎖就會升級成輕量鎖,輕量鎖的特點(diǎn)就是通過自旋CAS去獲取鎖。
  • 如果自旋獲取失敗,那么鎖就會升級成重量鎖,所有等待鎖的線程將被JVM掛起,在鎖釋放后,再由JVM統(tǒng)一通知喚醒,再去嘗試CAS鎖,如果失敗,繼續(xù)掛起。

很顯然,偏向鎖設(shè)計(jì)的目的是“在Java官方看來,對同一個(gè)鎖的爭搶大部分都發(fā)生在同個(gè)線程上”。
輕量鎖設(shè)計(jì)的目的是“在短期內(nèi),鎖的爭搶通過自旋CAS就可以獲取到,短時(shí)間內(nèi)的CPU自旋消耗小于線程掛起再喚醒的消耗”。
重量鎖就是最初優(yōu)化前的synchronized的邏輯了。

ReentrantLock

說到ReentrantLock,就不得不說到JUC里的AQS了。
AQS全稱AbstractQueueSynchronizer,幾乎JUC里所有的工具類,都依賴AQS實(shí)現(xiàn)。
AQS在java里,是一個(gè)抽象類,但是本質(zhì)上是一種思路在java中的實(shí)現(xiàn)而已。
AQS的實(shí)現(xiàn)邏輯如下:

  • 構(gòu)造一個(gè)隊(duì)列
  • 隊(duì)列中維護(hù)需要等待鎖的線程
  • 頭結(jié)點(diǎn)永遠(yuǎn)是持有鎖(或持有資源)的節(jié)點(diǎn),等待的節(jié)點(diǎn)在頭結(jié)點(diǎn)之后依次連接。
  • 頭結(jié)點(diǎn)釋放鎖后,會按照順序去喚醒那些等待的節(jié)點(diǎn),然后那些節(jié)點(diǎn)會再次去嘗試獲取鎖。

在synchronized鎖優(yōu)化以后,AQS的本質(zhì)與synchronized并沒有太大不同,兩者的性能也并沒有太大差距了,所以AQS現(xiàn)在的特點(diǎn)是:

  • 是在java api層面實(shí)現(xiàn)的鎖,所以可以實(shí)現(xiàn)各種并發(fā)工具類,操作也更加靈活
  • 因?yàn)樘峁┝顺瑫r(shí)時(shí)間等機(jī)制,操作靈活,所以不易死鎖。(相同的,如果發(fā)生死鎖,將更難排查,因?yàn)閖stack里將不會有deadlock標(biāo)識)。
  • 可以實(shí)現(xiàn)公平鎖,而synchronized必定是非公平鎖。
  • 因?yàn)槭荍avaApi層實(shí)現(xiàn)的鎖,所以可以響應(yīng)中斷。

到這里你會發(fā)現(xiàn),其實(shí)ReentrantLock可以說是synchronized在JavaApi層的實(shí)現(xiàn)。

Mysql 鎖

共享鎖(S) 與排它鎖(X)

作用范圍

這兩種鎖都包括行級鎖和表級鎖。
獲取共享鎖時(shí),如果該數(shù)據(jù)被其他事務(wù)的排它鎖鎖住,則無法獲取,需要等待排它鎖釋放。

意向鎖

作用范圍

意向鎖為表鎖,在獲取表鎖之前,一定會檢查意向鎖。

意圖鎖定協(xié)議如下:

在事務(wù)獲得表中某行的共享鎖之前,它必須首先獲得表上的 IS 鎖或更強(qiáng)的鎖。

在事務(wù)獲得表中行的排他鎖之前,它必須首先獲得表的 IX 鎖。

在獲取任意表鎖的共享鎖或排它鎖之前,一定會檢查該表上的共享鎖。

表鎖以及意向鎖的互斥規(guī)則如下:
X IX S IS
X Conflict Conflict Conflict Conflict
IX Conflict Compatible Conflict Compatible
S Conflict Conflict Compatible Compatible
IS Conflict Compatible Compatible Compatible

意向鎖的作用在于:在獲取表鎖時(shí),可以通過意向鎖來快速判斷能否獲取。

因?yàn)楂@取行級鎖時(shí),會先獲取對應(yīng)的意向鎖,這樣另外的事務(wù)在獲取表鎖時(shí)就可以通過意向鎖快速的判斷,而不需要每行去掃描。

特別注意的是,意向鎖是可以疊加的,即會存在多個(gè),如T1事務(wù)獲取了意向鎖IX1和行級鎖X1,T2事務(wù)依舊可以獲取意向鎖IX2和行級鎖X2,所以僅在獲取表級鎖之前,才會檢查意向鎖。

記錄鎖

記錄鎖生效在索引上,用以在SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE時(shí)保護(hù)該行數(shù)據(jù)不被其他事務(wù)更改。

記錄鎖在沒有索引時(shí)依舊會生效,因?yàn)閕nnodb會為每張表創(chuàng)建一個(gè)隱藏的索引。

記錄鎖是最基本的行鎖。

間隙鎖

間隙鎖生效在索引上,用于鎖定索引值后的行,防止插入,在select from table where index=? for update時(shí)會生效,例如index=1,則會鎖住index=1索引節(jié)點(diǎn)相關(guān)的行,防止其他事務(wù)插入數(shù)據(jù)

但是并不會防止update語句,哪怕update的數(shù)據(jù)不存在。

Next-Key Locks

這個(gè)鎖是記錄鎖和間隙鎖的組合,簡而言之在select from table where index=? for update時(shí),既會有間隙鎖防止insert,也會有記錄鎖在index上防止這一條數(shù)據(jù)的update和delete。這個(gè)Next-key只是對這兩種鎖的一種概括,因?yàn)檫@兩種鎖在select for update時(shí)通常會一起出現(xiàn)。

Insert Intention Locks

插入意向鎖,和意向鎖類似。不過是特殊的間隙鎖,并不發(fā)生在select for update,而是在同時(shí)發(fā)生insert時(shí)產(chǎn)生,例如在兩個(gè)事務(wù)同時(shí)insert索引區(qū)間為[4,7]時(shí),同時(shí)獲得該區(qū)間的意向鎖,此時(shí)事務(wù)不會阻塞,例如A:insert-5,B:insert-7,此時(shí)不會阻塞兩個(gè)事務(wù)。

插入意向鎖是一個(gè)特殊的間隙鎖,是為了防止正常間隙鎖鎖區(qū)間的情況下,insert頻繁阻塞而設(shè)計(jì)的,例如A:insert-5,B:insert-7,如果沒有插入意向鎖,那么5和7都要去嘗試獲取間隙鎖,此時(shí)第二個(gè)事務(wù)就會被阻塞,但是通過插入意向鎖,第二個(gè)事務(wù)就不會被阻塞,只有到插入的行確實(shí)沖突,才會被阻塞。

AUTO-INC Locks

自增鎖,這個(gè)鎖很明顯是表級insert鎖,為了保證自增主鍵的表的主鍵保持原子自增。

對于鎖這個(gè)東西,大家應(yīng)該多去理解各種鎖設(shè)計(jì)運(yùn)行的原理和模型,這樣在加深理解后,在使用起來才會更加深入和透徹。

常見鎖使用的場景和用法

double check

眾所周知,mysql的事務(wù)對防止重復(fù)插入并沒有什么卵用,唯一索引又存在很多缺點(diǎn),業(yè)務(wù)上最好不要使用,所以一般來說防止重復(fù)插入的通用做法就是使用分布式鎖,這就有一種比較常用的寫法。

final WeekendNoticeReadCountDO weekendNoticeReadCountDO = weekendNoticeReadRepositoryService.selectByNoticeId(noticeRequestDTO.getNoticeId());
if (weekendNoticeReadCountDO == null) {
    final String lockKey = RedisConstant.LOCK_WEEKEND_READ_COUNT_INSERT + ":" + noticeRequestDTO.getNoticeId();
    ClusterLock lock = clusterLockFactory.getClusterLockRedis(
        RedisConstant.REDIS_KEY_PREFIX,
        lockKey
    );
    if (lock.acquire(RedisConstant.REDIS_LOCK_DEFAULT_TIMEOUT)) {
        //double check
        final WeekendNoticeReadCountDO weekendNoticeReadCountDO = weekendNoticeReadRepositoryService.selectByNoticeId(noticeRequestDTO.getNoticeId());
        if (weekendNoticeReadCountDO == null) {
            try {
                lock.execute(() -> {
                    WeekendNoticeReadCountDO readCountDO = new WeekendNoticeReadCountDO();
                    readCountDO.setNoticeId(noticeRequestDTO.getNoticeId());
                    readCountDO.setReadCount(1L);
                    readCountDO.setCreateTime(new Date());
                    readCountDO.setUpdateTime(new Date());
                    weekendNoticeReadRepositoryService.insert(readCountDO);
                    return true;
                });
            } catch (ApiException err) {
                throw err;
            } catch (Exception e) {
                log.error("插入", e);
                throw new ApiException(ErrorEnum.SERVER_ERROR.getCode(), "服務(wù)端出錯(cuò)");
            }
        } else {
            weekendNoticeReadRepositoryService.noticeCountAdd(weekendNoticeReadCountDO);
        }
    } else {
        log.warn("redis鎖獲取超時(shí),key:{}", lockKey);
        throw new ApiException(ErrorEnum.SERVER_ERROR.getCode(), "服務(wù)器繁忙,請稍后重試");
    }
} 

在獲取到鎖之后,可能是經(jīng)過等待才獲取到的鎖,此時(shí)上一個(gè)釋放鎖的線程可能已經(jīng)插入了數(shù)據(jù)了,所以在鎖內(nèi)部,依舊要再次校驗(yàn)一下數(shù)據(jù)是否存在。
這種寫法適合大多數(shù)需要唯一性的寫場景。

避免死鎖

如何避免死鎖?最簡單有效的方法就是:**不要在鎖里再去獲取鎖,簡而言之就是鎖最好單獨(dú)使用,不要套娃。
也要注意一些隱性鎖,比如數(shù)據(jù)庫。
事務(wù)A:

  • 插入[5,7],插入意向鎖。
  • select for update更新[100,150],間隙鎖。
    事務(wù)B:
  • select for update更新[90,120],間隙鎖。
  • 插入[4,6],插入意向鎖。

此時(shí)在并發(fā)場景下,就可能會出現(xiàn)A持有了[5,7]的間隙鎖,在等待事務(wù)B[90,120]的間隙鎖,事務(wù)B也一樣,就死鎖了。
**

順帶談?wù)劜l(fā)場景下常見的問題

讀寫混亂

在寫業(yè)務(wù)代碼,定義一些工具類或者緩存類的時(shí)候,很容易疏忽而發(fā)生類似的問題。
比如構(gòu)建一個(gè)static緩存,沒有使用ConcurrentHashMap中的putIfAbsent等方法,也沒有加鎖去構(gòu)建,導(dǎo)致上面的線程剛put了,下面的線程就刪掉了,或者重復(fù)構(gòu)建2次緩存。

Redis或者一些并發(fā)操作釋放鎖或者資源,沒有檢查是否是當(dāng)前線程持有

這點(diǎn)在Redis鎖的示例代碼也講到了。
線程A獲取到鎖,此時(shí)B,C在等待,然后A執(zhí)行時(shí)間過長,導(dǎo)致鎖超時(shí)被自動釋放了,此時(shí)B獲取到了鎖,在快樂的執(zhí)行,然后A執(zhí)行完了之后,釋放鎖時(shí)沒有判斷是否還是自己持有,導(dǎo)致B持有的鎖被刪除了,此時(shí)C又獲取到了鎖,BC同時(shí)在執(zhí)行。

到此這篇關(guān)于Java與Mysql鎖相關(guān)知識總結(jié)的文章就介紹到這了,更多相關(guān)Java與Mysql鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IDEA中文亂碼的幾種常見解決方案

    IDEA中文亂碼的幾種常見解決方案

    IntelliJ IDEA 如果不進(jìn)行相關(guān)設(shè)置,可能會導(dǎo)致控制臺中文亂碼、配置文件中文亂碼等問題,非常影響編碼過程中進(jìn)行問題追蹤,所以本文給大家介紹了IDEA中文亂碼的幾種常見解決方案,需要的朋友可以參考下
    2025-04-04
  • Java深入講解SPI的使用

    Java深入講解SPI的使用

    SPI英文全稱為Service Provider Interface,顧名思義,服務(wù)提供者接口,它是jdk提供給“服務(wù)提供廠商”或者“插件開發(fā)者”使用的接口
    2022-06-06
  • Spring?Security認(rèn)證的完整流程記錄

    Spring?Security認(rèn)證的完整流程記錄

    相信大伙對Spring Security這個(gè)框架又愛又恨,愛它的強(qiáng)大,恨它的繁瑣,下面這篇文章主要給大家介紹了關(guān)于Spring?Security認(rèn)證流程的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-01-01
  • idea 創(chuàng)建 maven web 工程流程(圖文教程)

    idea 創(chuàng)建 maven web 工程流程(圖文教程)

    這篇文章主要介紹了idea 創(chuàng)建 maven web 工程流程(圖文教程),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05
  • 詳解springboot中mybatis注解形式

    詳解springboot中mybatis注解形式

    在本文中小編給大家分享了關(guān)于springboot中mybatis注解形式的介紹,有興趣的可以跟著學(xué)習(xí)下。
    2018-10-10
  • 使用Java實(shí)現(xiàn)系統(tǒng)托盤功能的介紹(附源碼以及截圖)

    使用Java實(shí)現(xiàn)系統(tǒng)托盤功能的介紹(附源碼以及截圖)

    本篇文章介紹了,在Java中實(shí)現(xiàn)系統(tǒng)托盤功能的詳解,文中附源碼以及截圖介紹。需要的朋友參考下
    2013-05-05
  • 實(shí)現(xiàn)一個(gè)規(guī)則引擎的可視化具體方案

    實(shí)現(xiàn)一個(gè)規(guī)則引擎的可視化具體方案

    項(xiàng)目原因需要用到規(guī)則引擎,但是發(fā)現(xiàn)大部分不可以自由的進(jìn)行規(guī)則定義,通過不斷嘗試變換關(guān)鍵字在搜索引擎搜索,最終在stackoverflow找到了一個(gè)探討這個(gè)問題的帖子,特此將帖子中提到的方案分享一下,如果你跟我一樣在研究同樣的問題,也許對你有用
    2021-04-04
  • 如何解決maven搭建一直處于running:..狀態(tài)問題

    如何解決maven搭建一直處于running:..狀態(tài)問題

    在使用Maven搭建項(xiàng)目時(shí),有時(shí)會遇到一直處于加載狀態(tài)的情況,通過修改設(shè)置可以解決這個(gè)問題,具體步驟為:1. 打開File->Settings->Build, Execution, Deployment->Maven->running,然后在VMOptions中填寫"-DarchetypeCatalog=internal"
    2024-09-09
  • 詳解SpringBoot?Start組件開發(fā)之記錄接口日志信息

    詳解SpringBoot?Start組件開發(fā)之記錄接口日志信息

    這篇文章主要為大家介紹了SpringBoot-Start組件開發(fā)之記錄接口日志信息詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • java實(shí)現(xiàn)線性表及其算法

    java實(shí)現(xiàn)線性表及其算法

    線性表是最簡單和最常用的一種數(shù)據(jù)結(jié)構(gòu),它是有n個(gè)體數(shù)據(jù)元素(節(jié)點(diǎn))組成的有限序列,這篇文章主要介紹了java實(shí)現(xiàn)線性表及其算法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-06-06

最新評論

玉环县| 大庆市| 隆子县| 大方县| 泰宁县| 上虞市| 武义县| 南宫市| 航空| 正蓝旗| 无为县| 原平市| 兰州市| 张家川| 康平县| 嘉义市| 翁源县| 光泽县| 宕昌县| 长宁区| 徐汇区| 武胜县| 独山县| 扶余县| 包头市| 临湘市| 都昌县| 西安市| 台山市| 宽城| 水城县| 桂林市| 化隆| 连城县| 西昌市| 上蔡县| 波密县| 罗山县| 比如县| 高唐县| 桃园县|