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

Java內(nèi)存模型JMM與volatile

 更新時(shí)間:2022年07月29日 14:25:06   作者:Hz488  
這篇文章主要介紹了Java內(nèi)存模型JMM與volatile,Java內(nèi)存模型是一種抽象的概念,并不真實(shí)存在,它描述的是一組規(guī)則或規(guī)范,定義了程序中各個(gè)變量的訪問(wèn)方式

1.Java內(nèi)存模型

JAVA定義了一套在多線程讀寫共享數(shù)據(jù)時(shí)時(shí),對(duì)數(shù)據(jù)的可見性、有序性和原子性的規(guī)則和保障。屏蔽掉不同操作系統(tǒng)間的微小差異。

Java內(nèi)存模型(Java Memory Model)是一種抽象的概念,并不真實(shí)存在,它描述的是一組規(guī)則或規(guī)范(定義了程序中各個(gè)變量的訪問(wèn)方式)。 JVM運(yùn)行程序的實(shí)體是線程,而每個(gè)線程創(chuàng)建時(shí) JVM 都會(huì)為其創(chuàng)建一個(gè)工作內(nèi)存(??臻g),用于存儲(chǔ)線程私有的數(shù)據(jù),而Java 內(nèi)存模型中規(guī)定所有變量都存儲(chǔ)在主內(nèi)存主內(nèi)存是共享內(nèi)存區(qū)域,所有線程都可以訪問(wèn), 但線程對(duì)變量的操作(讀取賦值等)必須在工作內(nèi)存中進(jìn)行。所以首先要將變量從主內(nèi)存拷貝的自己的工作內(nèi)存空間,然后對(duì)變量進(jìn)行操作,操作完成后再將變量寫回主內(nèi)存,不能直接操作主內(nèi)存中的變量。工作內(nèi)存是每個(gè)線程的私有數(shù)據(jù)區(qū)域,因此不同的線程間無(wú)法訪問(wèn)對(duì)方的工作內(nèi)存,線程間的通信(傳值)必須通過(guò)主內(nèi)存來(lái)完成。

基于JMM規(guī)范的線程,工作內(nèi)存,主內(nèi)存工作交互圖 :

  • 主內(nèi)存: 線程的共享數(shù)據(jù)區(qū)域,主要存儲(chǔ)的是Java實(shí)例對(duì)象,所有線程創(chuàng)建的實(shí)例對(duì)象都存放在主內(nèi)存中(包括局部變量、類信息、常量、靜態(tài)變量)。
  • 工作內(nèi)存: 線程私有,主要存儲(chǔ)當(dāng)前方法的所有本地變量信息(主內(nèi)存中的變量副本拷貝) , 每個(gè)線程只能訪問(wèn)自己的工作內(nèi)存,即線程中的本地變量對(duì)其它線程是不可見的,即使訪問(wèn)的是同一個(gè)共享變量。

對(duì)于一個(gè)實(shí)例對(duì)象中的成員方法: 如果方法中包含本地變量是基本數(shù)據(jù)類型,將直接存儲(chǔ)在工作內(nèi)存的幀棧結(jié)構(gòu)中,如果是引用類型,那么該變量的引用會(huì)存儲(chǔ)在功能內(nèi)存的幀棧中,而對(duì)象實(shí)例將存儲(chǔ)在主內(nèi)存(共享數(shù)據(jù)區(qū)域,堆)中。

需要注意的是,在主內(nèi)存中的實(shí)例對(duì)象可以被多線程共享,倘若兩個(gè)線程同時(shí)調(diào)用了同一個(gè)對(duì)象的同一個(gè)方法,那么兩條線程會(huì)將要操作的數(shù)據(jù)拷貝一份到自己的工作內(nèi)存中,執(zhí)行完成操作后才刷新到主內(nèi)存

下面是線程讀取共享變量count執(zhí)行count + 1 操作的過(guò)程:

數(shù)據(jù)同步八大原子操作:

  • (1)lock(鎖定): 作用于主內(nèi)存的變量,把一個(gè)變量標(biāo)記為一條線程獨(dú)占狀態(tài)
  • (2)unlock(解鎖): 作用于主內(nèi)存的變量,把一個(gè)處于鎖定狀態(tài)的變量釋放出來(lái),釋放后 的變量才可以被其他線程鎖定
  • (3)read(讀取): 作用于主內(nèi)存的變量,把一個(gè)變量值從主內(nèi)存?zhèn)鬏數(shù)骄€程的工作內(nèi)存 中,以便隨后的load動(dòng)作使用
  • (4)load(載入): 作用于工作內(nèi)存的變量,它把read操作從主內(nèi)存中得到的變量值放入工 作內(nèi)存的變量副本中
  • (5)use(使用): 作用于工作內(nèi)存的變量,把工作內(nèi)存中的一個(gè)變量值傳遞給執(zhí)行引擎
  • (6)assign(賦值): 作用于工作內(nèi)存的變量,它把一個(gè)從執(zhí)行引擎接收到的值賦給工作內(nèi) 存的變量
  • (7)store(存儲(chǔ)): 作用于工作內(nèi)存的變量,把工作內(nèi)存中的一個(gè)變量的值傳送到主內(nèi)存 中,以便隨后的write的操作
  • (8)write(寫入): 作用于工作內(nèi)存的變量,它把store操作從工作內(nèi)存中的一個(gè)變量的值 傳送到主內(nèi)存的變量中

2.并發(fā)三大特性

2.1.原子性

定義: 一個(gè)操作在CPU中不可以中途暫停再調(diào)度,要么全部執(zhí)行完成,要么全部都不執(zhí)行

問(wèn)題: 兩個(gè)線程對(duì)初始值的靜態(tài)變量一個(gè)做自增,一個(gè)做自減同樣做10000次的結(jié)果很可能不是 0

解決關(guān)鍵字: synchronized、ReentrantLock 建議:

  • 用sychronized對(duì)對(duì)象加鎖的力度建議大一點(diǎn)(減少加解鎖次數(shù))
  • 鎖住同一個(gè)對(duì)象

2.2.可見性

定義: 當(dāng)多個(gè)線程訪問(wèn)同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值,其他線程立即看得到修改的值。(即時(shí)性)

問(wèn)題: 兩個(gè)線程在不同的 CPU ,若線程1改變了變量 i 的值,還未刷新到主存,線程2又使用了 i,那么線程2看到的這個(gè)值肯定還是之前的

//線程1
boolean stop = false;
while(stop){
    ....
}
//線程2
stop = true;
//并未退出循環(huán)

解決關(guān)鍵字: synchronized、volatile

volatile 關(guān)鍵字,它可以用來(lái)修飾成員變量和靜態(tài)成員變量,避免線程從自己的工作內(nèi)存中查找變量值,必須到主存中獲取它的值,線程操作volatile變量都是直接操作主內(nèi)存,還可以禁止指令重排。 synchronized語(yǔ)句塊既可以保證代碼的原子性,也可以保證代碼塊內(nèi)部的可見性,但是呢synchronized屬于重量級(jí)操作,性能相對(duì)更低

注意: 對(duì)于上述循環(huán)代碼塊,加入System.out.println(); 會(huì)退出循環(huán),因?yàn)?println 被 synchronized 修飾,所有,不要隨便在代碼中使用這種打印語(yǔ)句,會(huì)極度影響程序性能。

2.3.有序性

定義: 虛擬機(jī)在進(jìn)行代碼編譯時(shí),對(duì)改變順序后不會(huì)對(duì)最終結(jié)果造成影響的代碼,虛擬機(jī)不一定會(huì)按我們寫的代碼順序運(yùn)行,有可能進(jìn)行重排序。實(shí)際上雖然重排后不會(huì)對(duì)變量值有影響,但會(huì)造成線程安全問(wèn)題。

解決關(guān)鍵字: synchronized、ReentrantLock  volatile關(guān)鍵字,可以禁止指令重排

指令重排: JIT 編譯器在運(yùn)行時(shí)的一些優(yōu)化,可以提升 CPU 的執(zhí)行效率,不讓 CPU 空閑下來(lái)。對(duì)改變順序后不會(huì)對(duì)最終結(jié)果造成影響的代碼,虛擬機(jī)不一定會(huì)按我們寫的代碼順序運(yùn)行,有可能進(jìn)行重排序。比如說(shuō),我兩行代碼 X 和 Y,虛擬機(jī)認(rèn)為它們倆的執(zhí)行順序不影響程序結(jié)果,但 Y 已經(jīng)在 CacheLine 中存在了,就會(huì)優(yōu)先執(zhí)行 Y。

分析下面?zhèn)未a的運(yùn)行情況(r.r1的值):

int num = 0;
boolean ready = false;
// 線程1 執(zhí)行此方法
public void action1(I_Result r) {
    if(ready) {
        r.r1 = num + num;
    } else {
        r.r1 = 1;
    }
}
// 線程2 執(zhí)行此方法
public void action2(I_Result r) {
    num = 2;
    ready = true;
}
情況1:線程1 先執(zhí)行,此時(shí) ready = false,所有進(jìn)入else ,結(jié)果為1
情況2:線程2 先執(zhí)行 num = 2,但還沒(méi)來(lái)得及執(zhí)行 ready = true,線程1 開始執(zhí)行,還是進(jìn)入else ,結(jié)果為1
情況3:線程2 先執(zhí)行到ready = true,線程1 執(zhí)行,進(jìn)入else ,結(jié)果為4
情況4:指令重排導(dǎo)致,線程2執(zhí)行 ready = true,切換到線程1,進(jìn)入 if 分支,相加為0,再切回線程2,執(zhí)行 num = 2,結(jié)果為0

double-checked locking 單例模式: 也存在指令重排問(wèn)題(不使用volatile,對(duì)象實(shí)例化是原子操作,但分為幾步,每一步又不是原子操作),因此需要在對(duì)象前加上 volatile 關(guān)鍵字防止指令重排,這也是個(gè)非常經(jīng)典的禁止指令重排的例子。

public class SingleLazy {
 ? ?private SingleLazy() {}
 ? ?private volatile static SingleLazy INSTANCE;
    // 獲取實(shí)體
 ? ?public static SingleLazy getInstance() {
 ? ? ? ?// 實(shí)例未被創(chuàng)建,開啟同步代碼塊準(zhǔn)備創(chuàng)建
 ? ? ? ?if (INSTANCE == null) {
 ? ? ? ? ? ?synchronized (SingleLazy.class) {
 ? ? ? ? ? ? ? ?// 也許其他線程在判斷完后已經(jīng)創(chuàng)建,再次判斷
 ? ? ? ? ? ? ? ?if (INSTANCE == null) {
 ? ? ? ? ? ? ? ? ? ?INSTANCE = new SingleLazy();
 ? ? ? ? ? ? ?  }
 ? ? ? ? ?  }
 ? ? ?  }
 ? ? ? ?return INSTANCE;
 ?  }
}

創(chuàng)建對(duì)象可以大致分為三步,其中第一步和第二步可能會(huì)發(fā)生指令重排導(dǎo)致安全性問(wèn)題:

memory = allocate();//1.分配對(duì)象內(nèi)存空間
instance(memory);//2.初始化對(duì)象
instance = memory;//3.設(shè)置instance指向剛分配的內(nèi)存地址,此時(shí)instance e != null

注意: JDK1.5前的 volatile 關(guān)鍵字不保證指令重排問(wèn)題

3.兩個(gè)規(guī)則

as-if-serial 語(yǔ)義保證單線程內(nèi)程序的執(zhí)行結(jié)果不被改變,happens-before關(guān)系保證正確同步的多線程程序的執(zhí)行結(jié)構(gòu)不被改變

3.1.happens-before規(guī)則

定義: 如果一個(gè)操作 happens-before 另一個(gè)操作,那么第一個(gè)操作的執(zhí)行結(jié)果將對(duì)第二個(gè)操作可見,而且第一個(gè)操作的執(zhí)行順序排在第二個(gè)操作之前。

兩個(gè)操作之間存在 happens-before 關(guān)系,并不意味 java 平臺(tái)的具體實(shí)現(xiàn)必須按照 happens-before 關(guān)系指定的順序來(lái)執(zhí)行。如果重排序后的執(zhí)行結(jié)構(gòu),與按 happens-before 關(guān)系來(lái)執(zhí)行的結(jié)果一致,那么這種重排序并不非法(JMM允許這種重排序),happens-before 原則內(nèi)容如下:

程序順序原則 即在一個(gè)線程內(nèi)必須保證語(yǔ)義串行性,也就是說(shuō)按照代碼順序執(zhí)行,(時(shí)間上)先執(zhí)行的操作happen-before(時(shí)間上后執(zhí)行的操作)

鎖規(guī)則 解鎖(unlock)操作必然發(fā)生在后續(xù)的同一個(gè)鎖的加鎖(lock)之前,也就是說(shuō),如果對(duì)于一個(gè)鎖解鎖后,再加鎖,那么加鎖的動(dòng)作必須在解鎖動(dòng)作之后(同一個(gè)鎖)。

volatile規(guī)則 volatile變量的寫,先發(fā)生于讀,這保證了volatile變量的可見性,簡(jiǎn) 單的理解就是,volatile變量在每次被線程訪問(wèn)時(shí),都強(qiáng)迫從主內(nèi)存中讀該變量的 值,而當(dāng)該變量發(fā)生變化時(shí),又會(huì)強(qiáng)迫將最新的值刷新到主內(nèi)存,任何時(shí)刻,不同的線程總是能夠看到該變量的最新值。

線程啟動(dòng)規(guī)則 線程的start()方法先于它的每一個(gè)動(dòng)作,即如果線程A在執(zhí)行線程B 的start方法之前修改了共享變量的值,那么當(dāng)線程B執(zhí)行start方法時(shí),線程A對(duì)共享 變量的修改對(duì)線程B可見

傳遞性 A先于B ,B先于C 那么A必然先于C

線程終止規(guī)則 線程的所有操作先于線程的終結(jié),Thread.join()方法的作用是等待 當(dāng)前執(zhí)行的線程終止。假設(shè)在線程B終止之前,修改了共享變量,線程A從線程B的 join方法成功返回后,線程B對(duì)共享變量的修改將對(duì)線程A可見。

線程中斷規(guī)則 對(duì)線程 interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測(cè)到 中斷事件的發(fā)生,可以通過(guò)Thread.interrupted()方法檢測(cè)線程是否中斷。

對(duì)象終結(jié)規(guī)則 對(duì)象的構(gòu)造函數(shù)執(zhí)行,結(jié)束先于finalize()方法

3.2.as-if-serial

不管怎么重排序(編譯器和處理器為了提高并行度),(單線程)程序的執(zhí)行結(jié)構(gòu)不能被改變。為了遵守as-if-serial 語(yǔ)義,編譯器和處理器不會(huì)存在數(shù)據(jù)依賴關(guān)系的操作做重排序,但是如果操作之前不存在數(shù)據(jù)依賴關(guān)系,這些操作就可能被編譯器和處理器重排序。

4.volatile

volatile是Java虛擬機(jī)提供的輕量級(jí)的同步機(jī)制,可以保證可見性,但無(wú)法保證原子性。  作用:

  • 保證可見性,也就是當(dāng)一個(gè)線程修改了一個(gè)被volatile修飾共享變量的值,新值總是可以被其他線程立即得知。即時(shí)可見通過(guò)緩存一致性協(xié)議保證。
  • 禁止指令重排優(yōu)化。通過(guò)內(nèi)存屏障實(shí)現(xiàn)。
//示例
//并發(fā)場(chǎng)景下,count++操作不具備原子性,分為兩步先讀取值,再寫回,會(huì)出現(xiàn)線程安全問(wèn)題
public class VolatileVisibility {
    public static volatile int count = 0;
    public static void increase(){
        count++;
    }
}

4.1.volatile 禁止重排優(yōu)化的實(shí)現(xiàn)

volatile 變量通過(guò)內(nèi)存屏障實(shí)現(xiàn)其可見性和禁止重排優(yōu)化。

內(nèi)存屏障: 又稱內(nèi)存柵欄,是一個(gè)CPU指令,它的作用有兩個(gè),一是保證特定操作的執(zhí)行順序,二是保證某些變量的內(nèi)存可見性。編譯器和處理器都能執(zhí)行指令重排優(yōu)化。Intel 硬件提供了一系列的內(nèi)存屏障,主要有:Ifence(讀屏障)、sfence(寫屏障)、mfence(全能屏障,包括讀寫)、Lock前綴等。不同的硬件實(shí)現(xiàn)內(nèi)存屏障的方式不同,Java 內(nèi)存模型屏蔽了這種底層硬件平臺(tái)的差異,由 JVM 來(lái)為不同的平臺(tái)生成相應(yīng)的機(jī)器碼。 JVM 中提供了四類內(nèi)存屏障指令:

屏障類型指令說(shuō)明
LoadLoadLoad1; LoadLoad; Load2保證load1的讀取操作在load2及后續(xù)讀取操作之前執(zhí)行
StoreStoreLoad1; LoadLoad; Load2在store2及其后的寫操作執(zhí)行前,保證store1的寫操作
StoreStoreStore1; StoreStore; Store2在stroe2及其后的寫操作執(zhí)行前,保證load1的讀操作
StoreLoadStore1; StoreLoad; Load2保證store1的寫操作已刷新到主內(nèi)存之后,load2及其作

volatile內(nèi)存語(yǔ)義的實(shí)現(xiàn): JMM 針對(duì)編譯器制定的 volatile 重排序規(guī)則表

操作普通讀寫volatile讀volatile寫
普通讀寫可以重排可以重排不可以重排
volatile讀不可以重排不可以重排不可以重排
volatile寫可以重排不可以重排不可以重排

比如第二行最后一個(gè)單元格的意思是:在程序中,當(dāng)?shù)谝粋€(gè)操作為普通變量的讀或?qū)憰r(shí),如果第二個(gè)操作為volatile寫,則編譯器不能重排序這兩個(gè)操作。 

編譯器在生成字節(jié)碼時(shí),會(huì)在指令序列中插入內(nèi)存屏障來(lái)禁止特定類型的處理器重排序:

  • 在每個(gè)volatile寫操作的前面插入一個(gè)StoreStore屏障
  • 在每個(gè)volatile寫操作的后面插入一個(gè)StoreLoad屏障
  • 在每個(gè)volatile讀操作的后面插入一個(gè)LoadLoad屏障
  • 在每個(gè)volatile讀操作的后面插入一個(gè)LoadStore屏障

class VolatileBarrierExample {
        int a;
        volatile int v1 = 1;
        volatile int v2 = 2;
        void readAndWrite() {
        int i = v1; // 第一個(gè)volatile讀、普通寫
        int j = v2; // 第二個(gè)volatile讀、普通寫
        a = i + j; // 普通寫 
        v1 = i + 1; // 第一個(gè)volatile寫
        v2 = j * 2; // 第二個(gè) volatile寫
    }
}

4.2.MESI緩存一致性協(xié)議

鏈接: 認(rèn)識(shí)Java底層操作系統(tǒng)與并發(fā)基礎(chǔ).  多核CPU的情況下,如何保證緩存內(nèi)部數(shù)據(jù)的一致性?JAVA引入了MESI緩存一致性協(xié)議。 

Java代碼的執(zhí)行流程:

volatile 修飾的變量(鎖也是)翻譯的匯編指令前會(huì)加 Lock 前綴,OS調(diào)度時(shí)會(huì) 觸發(fā)硬件緩存鎖定機(jī)制(總線鎖 或 緩存一致性協(xié)議) ,CPU 通過(guò)總線橋訪問(wèn)內(nèi)存條,多個(gè) CPU 訪問(wèn)同一內(nèi)存,首先需要拿到總線權(quán)。早期,計(jì)算機(jī)不發(fā)達(dá),性能低,總線鎖采用直接占有,其他 CPU 無(wú)法繼續(xù)通過(guò)總線橋訪問(wèn)。無(wú)法發(fā)揮 CPU 的多核能力?,F(xiàn)代 CPU 采用采用緩存一致性協(xié)議進(jìn)行保證(跨緩存行CacheLine(緩存存儲(chǔ)數(shù)據(jù)的數(shù)據(jù)單元) 時(shí)會(huì)升級(jí)為總線鎖)。

MESI 是指4種狀態(tài)的首字母。每個(gè) Cache line 有4個(gè)狀態(tài),可用2個(gè)bit表示:

狀態(tài)描述監(jiān)聽任務(wù)
M 修改(Modified)該CacheLine有效,數(shù)據(jù)被修改了,和內(nèi)存中的數(shù)據(jù)不一致,數(shù)據(jù)只存在于本Cache中緩存行必須時(shí)刻監(jiān)聽所有試圖讀該緩存行相對(duì)就主存的操作,這種操作必須在緩存將該緩存行寫回主存并將狀態(tài)變成S(共享)狀態(tài)之前被延遲執(zhí)行
E 獨(dú)享、互斥(Exclusive)該CacheLine有效,數(shù)據(jù)和內(nèi)存中的數(shù)據(jù)一致,數(shù)據(jù)只存在于本Cache中緩存行也必須監(jiān)聽其它緩存讀主存中該緩存行的操作,一旦有這種操作,該緩存行需要變成S(共享)狀態(tài)
S 共享 (Shared)該CacheLine有效,數(shù)據(jù)和內(nèi)存中的數(shù)據(jù)一致,數(shù)據(jù)存在于很多Cache中緩存行也必須監(jiān)聽其它緩存使該緩存行無(wú)效或者獨(dú)享該緩存行的請(qǐng)求,并將該緩存行變成無(wú)效(Invalid)
I 無(wú)效 (Invalid)該CacheLine無(wú)效無(wú)

MESI 協(xié)議狀態(tài)切換過(guò)程分析:

舉例:

注意:一個(gè) CacheLine 裝不下變量,會(huì)升級(jí)為總線鎖。

MESI優(yōu)化和他們引入的問(wèn)題:  緩存的一致性消息傳遞是要時(shí)間的,這就使其切換時(shí)會(huì)產(chǎn)生延遲。當(dāng)一個(gè)緩存被切換狀態(tài)時(shí)其他緩存收到消息完成各自的切換并且發(fā)出回應(yīng)消息這么一長(zhǎng)串的時(shí)間中CPU都會(huì)等待所有緩存響應(yīng)完成??赡艹霈F(xiàn)的阻塞都會(huì)導(dǎo)致各種各樣的性能問(wèn)題穩(wěn)定性問(wèn)題。

 為了避免這種CPU運(yùn)算能力的浪費(fèi),Store Bufferes 被引入使用。處理器把它想要寫入到主存的值寫到緩存,然后繼續(xù)去處理其他事情。當(dāng)所有失效確認(rèn)(Invalidate Acknowledge)都接收到時(shí),數(shù)據(jù)才會(huì)最 終被提交。

但它也會(huì)帶來(lái)一定的風(fēng)險(xiǎn):

  • 處理器會(huì)嘗試從存儲(chǔ)緩存(Store buffer)中讀取值,但它還沒(méi)有進(jìn)行提交。這個(gè)的解決方案稱為Store Forwarding,它使得加載的時(shí)候,如果存儲(chǔ)緩存中存在,則進(jìn)行返回
  • 保存什么時(shí)候會(huì)完成,這個(gè)并沒(méi)有任何保證,可能會(huì)發(fā)生重排序(非指令重排)。CPU會(huì)讀到跟程序中寫入的順序不一樣的結(jié)果。

到此這篇關(guān)于Java內(nèi)存模型JMM與volatile的文章就介紹到這了,更多相關(guān)Java內(nèi)存模型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java?中的?Lambda?List?轉(zhuǎn)?Map?的多種方法詳解

    Java?中的?Lambda?List?轉(zhuǎn)?Map?的多種方法詳解

    這篇文章主要介紹了Java中的Lambda?List轉(zhuǎn)Map幾種方式,傳統(tǒng)的方式又顯得太臃腫,于是就想到 Lambda 神器,今天我們就來(lái)看看都有哪幾種轉(zhuǎn)換方式(List -> Map),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2022-07-07
  • java web response提供文件下載功能的實(shí)例講解

    java web response提供文件下載功能的實(shí)例講解

    下面小編就為大家分享一篇java web response提供文件下載功能的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • springboot 集成redis哨兵主從的實(shí)現(xiàn)

    springboot 集成redis哨兵主從的實(shí)現(xiàn)

    本文主要介紹了springboot 集成redis哨兵主從的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • springboot項(xiàng)目配置多數(shù)據(jù)庫(kù)連接的示例詳解

    springboot項(xiàng)目配置多數(shù)據(jù)庫(kù)連接的示例詳解

    這篇文章主要介紹了springboot項(xiàng)目配置多數(shù)據(jù)庫(kù)連接的示例,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-12-12
  • 詳解在springmvc中解決FastJson循環(huán)引用的問(wèn)題

    詳解在springmvc中解決FastJson循環(huán)引用的問(wèn)題

    本篇文章主要介紹了在springmvc中解決FastJson循環(huán)引用的問(wèn)題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-01-01
  • java文件上傳Demo(必看篇)

    java文件上傳Demo(必看篇)

    下面小編就為大家?guī)?lái)一篇java文件上傳Demo(必看篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • Java Convert Kotlin空指針異常的解決方法

    Java Convert Kotlin空指針異常的解決方法

    本文主要介紹了Java?Convert?Kotlin空指針異常的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 在Spring Boot中如何使用Cookies詳析

    在Spring Boot中如何使用Cookies詳析

    這篇文章主要給大家介紹了關(guān)于在Spring Boot中如何使用Cookies的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • CentOS?7.9服務(wù)器Java部署環(huán)境配置的過(guò)程詳解

    CentOS?7.9服務(wù)器Java部署環(huán)境配置的過(guò)程詳解

    這篇文章主要介紹了CentOS?7.9服務(wù)器Java部署環(huán)境配置,主要包括ftp服務(wù)器搭建過(guò)程、jdk安裝方法以及mysql安裝過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽超詳細(xì)講解

    SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽超詳細(xì)講解

    這篇文章主要介紹了SpringBoot工程啟動(dòng)順序與自定義監(jiān)聽,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-11-11

最新評(píng)論