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

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

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

一、并發(fā)下的線程安全問題

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

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

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è)線程并發(fā)執(zhí)行,分別對(duì)num自增1萬次和自減1萬次,按常理執(zhí)行完成后,num的值應(yīng)該仍然是0。然而實(shí)際的情況是,最終的值是多少并不確定,多運(yùn)行幾次,有時(shí)得個(gè)正數(shù),有時(shí)得個(gè)負(fù)數(shù)。

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

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

二、Java同步鎖的用法

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

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

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

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

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

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

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

下面簡(jiǎn)單演示開篇代碼的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ì)象來當(dāng)鎖,最好是和代碼邏輯以及共享數(shù)據(jù)有所關(guān)聯(lián),隨便找一個(gè)字符串來當(dāng)鎖是不負(fù)責(zé)任的。

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

三、Redis分布式鎖的原理

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

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

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

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

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

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

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

總結(jié)

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

相關(guān)文章

  • java中獲取類加載路徑和項(xiàng)目根路徑的5種方式分析

    java中獲取類加載路徑和項(xiàng)目根路徑的5種方式分析

    本篇文章介紹了,java中獲取類加載路徑和項(xiàng)目根路徑的5種方式分析。需要的朋友參考下
    2013-05-05
  • Java實(shí)現(xiàn)本地緩存的四種方法實(shí)現(xiàn)與對(duì)比

    Java實(shí)現(xiàn)本地緩存的四種方法實(shí)現(xiàn)與對(duì)比

    本地緩存的優(yōu)點(diǎn)就是速度非常快,沒有網(wǎng)絡(luò)消耗,本地緩存比如 caffine,guava cache 這些都是比較常用的,下面我們來看看這四種緩存的具體實(shí)現(xiàn)吧
    2025-08-08
  • Docker與Kubernetes部署Java應(yīng)用容器化實(shí)踐指南

    Docker與Kubernetes部署Java應(yīng)用容器化實(shí)踐指南

    容器化是一種將應(yīng)用程序及其依賴打包成一個(gè)可移植的單元的方法,容器能夠在任何環(huán)境中運(yùn)行,并且具有隔離性、可擴(kuò)展性和高效性,這篇文章主要介紹了Docker與Kubernetes部署Java應(yīng)用容器化實(shí)踐指南的相關(guān)資料
    2026-05-05
  • Spring中的@ModelAttribute模型屬性綁定詳解

    Spring中的@ModelAttribute模型屬性綁定詳解

    這篇文章主要介紹了Spring中的@ModelAttribute模型屬性綁定詳解,@ModelAttribute用于將方法參數(shù)或返回值綁定到Model屬性上,并公開給Web視圖,支持使用@RequestMapping注釋的Controller類,需要的朋友可以參考下
    2024-02-02
  • IDE 搭配 Lombok 注解的配置方法

    IDE 搭配 Lombok 注解的配置方法

    Lombok是一個(gè)Java庫(kù),通過注解簡(jiǎn)化代碼編寫,減少樣板代碼,在使用IDE如IntelliJ IDEA或Eclipse結(jié)合Lombok時(shí),需要先添加Lombok依賴,然后在IDE中配置,以啟用注解處理器,配置完成后,使用Lombok注解來簡(jiǎn)化Java類的編寫,本文介紹IDE搭配Lombok注解的配置,感興趣的朋友一起看看吧
    2025-03-03
  • java新人基礎(chǔ)入門之遞歸調(diào)用

    java新人基礎(chǔ)入門之遞歸調(diào)用

    這篇文章主要給大家介紹了關(guān)于java新人基礎(chǔ)入門之遞歸調(diào)用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • SpringBoot實(shí)現(xiàn)任意文件在線預(yù)覽功能

    SpringBoot實(shí)現(xiàn)任意文件在線預(yù)覽功能

    在現(xiàn)代工作與生活環(huán)境中,文件共享與查看已經(jīng)成為日常不可或缺的一部分,不過傳統(tǒng)的文件處理方式要求用戶下載文件到本地后才能查看,不僅占用了設(shè)備的存儲(chǔ)空間,還可能導(dǎo)致文件傳播效率低下,所以本文給大家介紹了SpringBoot實(shí)現(xiàn)任意文件在線預(yù)覽功能,需要的朋友可以參考下
    2025-07-07
  • 淺談Java中的集合存儲(chǔ)數(shù)據(jù)后,輸出數(shù)據(jù)的有序和無序問題

    淺談Java中的集合存儲(chǔ)數(shù)據(jù)后,輸出數(shù)據(jù)的有序和無序問題

    這篇文章主要介紹了淺談Java中的集合存儲(chǔ)數(shù)據(jù)后,輸出數(shù)據(jù)的有序和無序問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Java 8日期處理LocalDateTime時(shí)區(qū)轉(zhuǎn)換的3大誤區(qū)與解決方案

    Java 8日期處理LocalDateTime時(shí)區(qū)轉(zhuǎn)換的3大誤區(qū)與解決方案

    文章介紹了Java 8中的日期時(shí)間API,包括`LocalDateTime`、`ZonedDateTime`和`Instant`等核心類,以及它們?cè)诓煌瑘?chǎng)景下的應(yīng)用,文章還探討了常見誤區(qū),本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • Springboot集成Hutool導(dǎo)出CSV的方法

    Springboot集成Hutool導(dǎo)出CSV的方法

    文章介紹了如何引入依賴、使用CSV工具類以及實(shí)現(xiàn)接口的相關(guān)內(nèi)容,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2026-01-01

最新評(píng)論

北川| 普兰店市| 双辽市| 台南县| 泗阳县| 乐清市| 安达市| 澜沧| 广西| 丹东市| 比如县| 腾冲县| 汝南县| 山东| 德庆县| 津南区| 蓬溪县| 兴海县| 寿光市| 北安市| 玉门市| 德昌县| 凌海市| 辽阳县| 绍兴县| 重庆市| 胶州市| 水富县| 阳城县| 耒阳市| 安丘市| 文成县| 宁蒗| 清新县| 宝清县| 肇州县| 启东市| 榆社县| 包头市| 大连市| 永寿县|