欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java鎖和分布式鎖的用法及解讀

 更新時(shí)間:2025年07月21日 09:55:36   作者:壞貓警長(zhǎng)  
文章闡述了線(xiàn)程安全問(wèn)題、Java同步鎖的使用及Redis分布式鎖的原理,核心內(nèi)容包括:多線(xiàn)程并發(fā)修改共享數(shù)據(jù)導(dǎo)致不一致,Java通過(guò)synchronized鎖保證原子性;Redis利用單線(xiàn)程特性和setnx命令實(shí)現(xiàn)跨進(jìn)程鎖,解決多節(jié)點(diǎn)數(shù)據(jù)同步問(wèn)題

一、并發(fā)下的線(xiàn)程安全問(wèn)題

所謂線(xiàn)程安全問(wèn)題,是指多個(gè)線(xiàn)程“同時(shí)”對(duì)同一個(gè)數(shù)據(jù)進(jìn)行修改時(shí),出現(xiàn)數(shù)據(jù)不一致等現(xiàn)象。

考慮如下這個(gè)線(xiàn)程不安全的情況:

public static int num = 0;
public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
   for (int i = 0; i < 10000; i++) {
   num++;
   }
   });
   //
   Thread t2 = new Thread(() -> {
   for (int i = 0; i < 10000; i++) {
   num--;
   }
   });
   //
   t1.start();
   t2.start();
   Thread.sleep(5000);
   System.out.println(num);
}

以上代碼中,num變量的初值為0,兩個(gè)線(xiàn)程并發(fā)執(zhí)行,分別對(duì)num自增1萬(wàn)次和自減1萬(wàn)次,按常理執(zhí)行完成后,num的值應(yīng)該仍然是0。然而實(shí)際的情況是,最終的值是多少并不確定,多運(yùn)行幾次,有時(shí)得個(gè)正數(shù),有時(shí)得個(gè)負(fù)數(shù)。

出現(xiàn)這種現(xiàn)象的原因是兩個(gè)線(xiàn)程對(duì)同一個(gè)數(shù)據(jù)num的操作會(huì)互相干擾,一個(gè)線(xiàn)程正用著num運(yùn)行到一半時(shí),另一個(gè)線(xiàn)程修改了num的值。這里的num自增自減雖然在java層面只有一行代碼,但是在cpu執(zhí)行時(shí)其實(shí)是多個(gè)步驟,至少包括讀取數(shù)值、計(jì)算加1、寫(xiě)回內(nèi)存,而這中間便可能發(fā)生線(xiàn)程切換,例如:線(xiàn)程一讀取了num的值為0,然后線(xiàn)程二也讀取了num的值為0,線(xiàn)程一計(jì)算+1再寫(xiě)回內(nèi)存值為1,線(xiàn)程二計(jì)算-1然后寫(xiě)回內(nèi)存值為-1,所以明明應(yīng)該得到結(jié)果為0,但是這里得到的值卻是-1,這里最本質(zhì)的問(wèn)題就是線(xiàn)程對(duì)共享數(shù)據(jù)num的操作不是原子性的。

這個(gè)示例中對(duì)共享數(shù)據(jù)num的操作只有一行代碼,實(shí)際項(xiàng)目中往往涉及到多行代碼的復(fù)雜處理,于是高并發(fā)情況下就很容易出現(xiàn)詭異的數(shù)據(jù)不一致問(wèn)題。

二、Java同步鎖的用法

如上所說(shuō),解決并發(fā)安全問(wèn)題的關(guān)鍵就是保證共享數(shù)據(jù)操作的原子性,在某個(gè)線(xiàn)程正在使用共享數(shù)據(jù)的過(guò)程中,避免其他線(xiàn)程操作這個(gè)共享數(shù)據(jù)。Java代碼層面最簡(jiǎn)單的方法就是加鎖,針對(duì)某個(gè)共享數(shù)據(jù),在所有準(zhǔn)備使用它的代碼段開(kāi)頭獲得一個(gè)鎖,用完了再把鎖釋放即可,如果拿不到鎖就說(shuō)明被其他線(xiàn)程拿去了,則代碼暫停執(zhí)行,等待其他線(xiàn)程釋放鎖,直到拿到了鎖再繼續(xù)執(zhí)行。

需要知道,獲得鎖和釋放鎖的動(dòng)作會(huì)由操作系統(tǒng)和硬件來(lái)保證其原子性,試想如果加解鎖動(dòng)作本身也不原子,那么線(xiàn)程安全問(wèn)題就無(wú)解了。Java加解鎖的synchronized方式和ReentrantLock方式原理相似,這里只介紹synchronized的用法。

Java中的任意Object對(duì)象都擁有一個(gè)鎖,包括Class對(duì)象,以下代碼演示Class對(duì)象鎖的使用:

要特別注意,不同類(lèi)加載器加載的同一個(gè)class文件,產(chǎn)生的Class對(duì)象并不是同一個(gè),當(dāng)然鎖也不是同一個(gè)。

以下代碼演示使用一個(gè)普通對(duì)象的鎖:

要特別注意,如果是同一個(gè)class類(lèi)型的不同對(duì)象執(zhí)行這個(gè)方法,在這里鎖當(dāng)然是不同的,各自是各自的鎖。

如下代碼不能保證線(xiàn)程安全,因?yàn)檫@100個(gè)線(xiàn)程中的obj對(duì)象不是同一個(gè),即各線(xiàn)程執(zhí)行methodSync方法時(shí)獲得的鎖不是同一個(gè):

下面簡(jiǎn)單演示開(kāi)篇代碼的synchronized處理辦法,只要保證給不安全的代碼塊加上同一個(gè)鎖即可,不論是同一個(gè)對(duì)象、同一個(gè)Class對(duì)象、同一個(gè)字符串對(duì)象。

public static int num = 0;
public static void main(String[] args) throws InterruptedException {
   Thread t1 = new Thread(() -> {
   synchronized ("鎖") {
   for (int i = 0; i < 10000; i++) {
   num++;
   }
   }
   });
   //
   Thread t2 = new Thread(() -> {
   synchronized ("鎖") {
   for (int i = 0; i < 10000; i++) {
   num--;
   }
   }
   });
   //
   t1.start();
   t2.start();
   Thread.sleep(5000);
   System.out.println(num);
}

真實(shí)項(xiàng)目中使用時(shí),鎖的選擇很重要,不要隨便找個(gè)對(duì)象來(lái)當(dāng)鎖,最好是和代碼邏輯以及共享數(shù)據(jù)有所關(guān)聯(lián),隨便找一個(gè)字符串來(lái)當(dāng)鎖是不負(fù)責(zé)任的。

這里應(yīng)該能感覺(jué)到,很多情況下用那個(gè)被共享的數(shù)據(jù)來(lái)當(dāng)鎖可能正合適

三、Redis分布式鎖的原理

Java的鎖只能管到自己的進(jìn)程,如果“共享數(shù)據(jù)”是多進(jìn)程共享的比如數(shù)據(jù)庫(kù)里的一個(gè)數(shù)據(jù),那么Java鎖就無(wú)效了,因?yàn)?strong>進(jìn)程之間無(wú)法找到同一個(gè)java對(duì)象來(lái)當(dāng)鎖,這時(shí)就需要分布式鎖來(lái)控制。當(dāng)然其原理本身,也是想辦法在進(jìn)程之間找到“同一個(gè)對(duì)象”(類(lèi)似二值信號(hào)量)來(lái)當(dāng)鎖。

于是redis就成了一個(gè)選擇,分布式系統(tǒng)中,各個(gè)應(yīng)用大家共用同一個(gè)redis,那不妨就模仿進(jìn)程級(jí)鎖來(lái)做一個(gè)分布式鎖,具體實(shí)現(xiàn)上考慮以下兩個(gè)方面:

1、在redis中找一個(gè)公共的數(shù)據(jù)來(lái)充當(dāng)鎖的角色,所有并發(fā)安全相關(guān)的代碼塊開(kāi)頭加鎖,結(jié)尾解鎖,加不上鎖就等待。

2、想辦法保證加鎖動(dòng)作的原子性,沒(méi)有了操作系統(tǒng)和硬件來(lái)保證,則必須自行設(shè)計(jì)一番。

使用redis來(lái)實(shí)現(xiàn)分布式鎖的代碼網(wǎng)上有很多,可以結(jié)合以上兩方面來(lái)分析其寫(xiě)法。這里只解釋一下為什么reids容易保證加鎖動(dòng)作的原子性:

如上圖,redis對(duì)數(shù)據(jù)的操作是單線(xiàn)程的,即由一個(gè)線(xiàn)程來(lái)執(zhí)行從外部傳進(jìn)來(lái)的諸多命令,一個(gè)命令一個(gè)命令排著執(zhí)行,先到的先執(zhí)行,后到的后執(zhí)行。所以只要加鎖的動(dòng)作能由一個(gè)命令來(lái)完成,則天然就能保證加鎖動(dòng)作的原子性,解鎖也是一樣。于是setnx命令就成了一個(gè)很好的選擇,單條命令就可以設(shè)置數(shù)據(jù)同時(shí)返回是否設(shè)置成功。

所以也就很好理解,如果要給鎖加失效時(shí)間或鎖持有者信息的話(huà),為什么實(shí)現(xiàn)上就變得復(fù)雜了,根本原因就是需要保證加解鎖動(dòng)作的原子性,避免高并發(fā)時(shí)加解鎖動(dòng)作執(zhí)行到一半時(shí)被別的進(jìn)程給鉆了空子。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解MyBatis工作原理

    詳解MyBatis工作原理

    近來(lái)想寫(xiě)一個(gè)mybatis的分頁(yè)插件,但是在寫(xiě)插件之前肯定要了解一下mybatis具體的工作原理吧,本文就詳細(xì)總結(jié)了MyBatis工作原理,,需要的朋友可以參考下
    2021-05-05
  • springboot亂碼問(wèn)題排查思路解析

    springboot亂碼問(wèn)題排查思路解析

    這篇文章主要為大家介紹了springboot亂碼問(wèn)題排查思路解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • SpringBoot使用@Scheduled實(shí)現(xiàn)定時(shí)任務(wù)的并行執(zhí)行

    SpringBoot使用@Scheduled實(shí)現(xiàn)定時(shí)任務(wù)的并行執(zhí)行

    在SpringBoot中,如果使用@Scheduled注解來(lái)定義多個(gè)定時(shí)任務(wù),默認(rèn)情況下這些任務(wù)將會(huì)被安排在一個(gè)單線(xiàn)程的調(diào)度器中執(zhí)行,這意味著,這些任務(wù)將會(huì)串行執(zhí)行,而不是并行執(zhí)行,本文介紹了SpringBoot使用@Scheduled實(shí)現(xiàn)定時(shí)任務(wù)的并行執(zhí)行,需要的朋友可以參考下
    2024-06-06
  • springboot利用redis、Redisson處理并發(fā)問(wèn)題的操作

    springboot利用redis、Redisson處理并發(fā)問(wèn)題的操作

    這篇文章主要介紹了springboot利用redis、Redisson處理并發(fā)問(wèn)題的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 深入解析Java設(shè)計(jì)模式編程中觀察者模式的運(yùn)用

    深入解析Java設(shè)計(jì)模式編程中觀察者模式的運(yùn)用

    這篇文章主要介紹了Java設(shè)計(jì)模式編程中觀察者模式的運(yùn)用,觀察者模式主要是為了將對(duì)象進(jìn)行與被觀察者一樣的實(shí)現(xiàn),需要的朋友可以參考下
    2016-02-02
  • SpringBoot工程創(chuàng)建的四種方式詳解

    SpringBoot工程創(chuàng)建的四種方式詳解

    本文介紹四種創(chuàng)建Spring Boot項(xiàng)目的方法(IDEA聯(lián)網(wǎng)、官網(wǎng)、阿里云源、手動(dòng)配置),并解答JDK版本、Maven設(shè)置等常見(jiàn)問(wèn)題,最終實(shí)現(xiàn)集成tess4j進(jìn)行圖片文本識(shí)別,感興趣的朋友一起看看吧
    2025-06-06
  • java實(shí)現(xiàn)一個(gè)接口調(diào)取另一個(gè)接口(接口一調(diào)取接口二)

    java實(shí)現(xiàn)一個(gè)接口調(diào)取另一個(gè)接口(接口一調(diào)取接口二)

    這篇文章主要介紹了java實(shí)現(xiàn)一個(gè)接口調(diào)取另一個(gè)接口(接口一調(diào)取接口二),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • MyBatis中的N+1問(wèn)題的解決方法

    MyBatis中的N+1問(wèn)題的解決方法

    本文主要介紹了MyBatis中的N+1問(wèn)題的四種解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • Mybatis實(shí)現(xiàn)自定義的typehandler三步曲

    Mybatis實(shí)現(xiàn)自定義的typehandler三步曲

    這篇文章主要介紹了Mybatis實(shí)現(xiàn)自定義的typehandler三步曲的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-07-07
  • @RequestBody 部分屬性沒(méi)有轉(zhuǎn)化成功的處理

    @RequestBody 部分屬性沒(méi)有轉(zhuǎn)化成功的處理

    這篇文章主要介紹了@RequestBody 部分屬性沒(méi)有轉(zhuǎn)化成功的處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10

最新評(píng)論