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

深入理解Java中的synchronized 和 ReentrantLock 底層原理

 更新時間:2025年11月28日 10:19:02   作者:北郭guo  
synchronized和ReentrantLock是Java中最核心的兩種鎖實現(xiàn),前者是JVM內(nèi)置鎖(隱式鎖),后者是JUC顯式鎖(基于AQS 框架),本文給大家介紹Java中的synchronized和ReentrantLock底層原理,感興趣的朋友跟隨小編一起看看吧

synchronized 和 ReentrantLock 是 Java 中最核心的兩種鎖實現(xiàn),前者是 JVM 內(nèi)置鎖(隱式鎖),后者是 JUC 顯式鎖(基于 AQS 框架)。兩者的實現(xiàn)底層依賴完全不同,但核心目標(biāo)一致 —— 通過「互斥」保證并發(fā)安全,下面從 底層依賴、核心流程、關(guān)鍵機(jī)制 三個維度,深入拆解它們的實現(xiàn)原理:

一、synchronized的實現(xiàn):JVM 層面的 “自動鎖”

synchronized 是 Java 語言原生支持的鎖,無需手動釋放,底層由 JVM 解釋器 + 操作系統(tǒng)內(nèi)核 協(xié)同實現(xiàn),核心依賴 對象頭(Mark Word) 和 監(jiān)視器(Monitor),并通過「鎖升級」機(jī)制優(yōu)化性能。

1. 核心依賴組件

(1)對象頭(Mark Word):鎖狀態(tài)的 “存儲載體”

Java 中所有對象都能作為鎖,本質(zhì)是因為對象在內(nèi)存中的「對象頭」包含了鎖相關(guān)的狀態(tài)信息。對象頭由兩部分組成:Mark Word(核心)和 Klass Pointer(指向類元數(shù)據(jù))。

Mark Word 是一個動態(tài)結(jié)構(gòu)(32 位 JVM 示例),會根據(jù)鎖狀態(tài)變化存儲不同信息,核心字段包括:

鎖狀態(tài)Mark Word 結(jié)構(gòu)(32 位)核心作用
無鎖狀態(tài)哈希碼(25 位) + 分代年齡(4 位) + 鎖標(biāo)志位(01)存儲對象哈希碼、GC 分代信息,無鎖標(biāo)識
偏向鎖線程 ID(23 位) + Epoch(2 位) + 分代年齡(4 位) + 鎖標(biāo)志位(01)記錄持有鎖的線程 ID,實現(xiàn) “無競爭時零開銷”
輕量級鎖指向棧幀中 “鎖記錄” 的指針(30 位) + 鎖標(biāo)志位(00)關(guān)聯(lián)線程棧中的鎖記錄,實現(xiàn)自旋鎖機(jī)制
重量級鎖指向 Monitor 的指針(30 位) + 鎖標(biāo)志位(10)關(guān)聯(lián)操作系統(tǒng)級別的 Monitor,實現(xiàn)阻塞同步

?? 關(guān)鍵:Mark Word 的「鎖標(biāo)志位」決定當(dāng)前鎖狀態(tài),JVM 通過修改 Mark Word 實現(xiàn)鎖的切換。

(2)監(jiān)視器(Monitor):重量級鎖的 “核心同步工具”

Monitor 是操作系統(tǒng)層面的「互斥同步原語」(也叫管程),是 synchronized 重量級鎖的核心依賴。每個 Java 對象在創(chuàng)建時,JVM 會為其關(guān)聯(lián)一個 Monitor(可理解為 “鎖對象”),Monitor 內(nèi)部包含三個核心結(jié)構(gòu):

  • 所有者(Owner):存儲當(dāng)前持有鎖的線程(唯一,保證互斥);
  • EntryList(阻塞隊列):存儲等待獲取鎖的線程(競爭失敗后進(jìn)入阻塞狀態(tài));
  • WaitSet(等待隊列):存儲調(diào)用 wait() 方法后釋放鎖的線程(需被 notify() 喚醒)。

?? 本質(zhì):Monitor 通過操作系統(tǒng)的「互斥量(Mutex)」實現(xiàn)線程阻塞 / 喚醒,是重量級鎖性能開銷的主要來源。

2.synchronized的核心實現(xiàn)流程(鎖升級)

synchronized 并非一開始就是重量級鎖,JVM 會根據(jù)「競爭激烈程度」自動觸發(fā)「鎖升級」(無鎖→偏向鎖→輕量級鎖→重量級鎖),目的是在 “無競爭” 和 “激烈競爭” 場景下都能保證性能。

(1)無鎖 → 偏向鎖(單線程競爭)

  • 觸發(fā)條件:只有一個線程嘗試獲取鎖。
  • 實現(xiàn)流程
    1. 線程 T1 第一次進(jìn)入同步塊時,JVM 通過 CAS 操作 修改對象的 Mark Word:將「線程 ID」寫入 Mark Word,鎖標(biāo)志位保持 01(偏向鎖標(biāo)識);
    2. 后續(xù) T1 再次進(jìn)入同步塊時,只需判斷 Mark Word 中的線程 ID 是否為自己:
      • 是:直接進(jìn)入(無需任何同步操作,零開銷);
      • 否:偏向鎖失效,觸發(fā)升級。
  • 核心目的:優(yōu)化 “單線程重復(fù)獲取鎖” 的場景(如單線程操作集合),避免無意義的同步開銷。

(2)偏向鎖 → 輕量級鎖(低并發(fā)交替競爭)

  • 觸發(fā)條件:有第二個線程 T2 嘗試獲取同一把鎖(T1 未釋放)。
  • 實現(xiàn)流程
    1. JVM 先暫停 T1,撤銷偏向鎖(將 Mark Word 恢復(fù)為無鎖狀態(tài));
    2. T1 和 T2 各自在自己的「棧幀」中創(chuàng)建一個「鎖記錄(Lock Record)」,鎖記錄中存儲對象 Mark Word 的副本(Displaced Mark Word);
    3. 兩個線程通過 CAS 操作 競爭修改對象的 Mark Word:將 Mark Word 指向自己的鎖記錄;
      • 成功:獲取輕量級鎖,進(jìn)入同步塊;
      • 失?。赫f明競爭存在,進(jìn)入自旋重試(循環(huán)嘗試 CAS,避免阻塞)。
  • 核心目的:優(yōu)化 “多線程交替執(zhí)行” 的場景(無激烈競爭),通過自旋減少線程阻塞 / 喚醒的開銷(用戶態(tài)操作,比內(nèi)核態(tài)高效)。

(3)輕量級鎖 → 重量級鎖(高并發(fā)激烈競爭)

  • 觸發(fā)條件:自旋次數(shù)超過閾值(JDK1.6 后為「自適應(yīng)自旋」,根據(jù)前一次自旋成功率動態(tài)調(diào)整),或有更多線程參與競爭。
  • 實現(xiàn)流程
    1. 自旋失敗的線程放棄競爭,將鎖升級為重量級鎖(修改 Mark Word 指向 Monitor);
    2. 線程進(jìn)入 Monitor 的 EntryList 隊列,放棄 CPU 資源(進(jìn)入 阻塞狀態(tài),由操作系統(tǒng)調(diào)度);
    3. 持有鎖的線程釋放鎖時,通過操作系統(tǒng)喚醒 EntryList 中的一個線程,該線程再次嘗試獲取鎖。
  • 核心目的:應(yīng)對 “高并發(fā)激烈競爭” 場景,通過阻塞機(jī)制避免 CPU 空轉(zhuǎn)(自旋會消耗 CPU)。

3. 解鎖與自動釋放機(jī)制

synchronized 無需手動解鎖,JVM 會在以下場景自動釋放鎖:

  • 線程退出同步塊(synchronized 代碼塊或方法);
  • 線程拋出異常時(JVM 會在異常處理中插入解鎖指令)。

解鎖的核心操作:

  • 輕量級鎖:通過 CAS 將 Mark Word 恢復(fù)為「Displaced Mark Word」(鎖記錄中的副本);
  • 重量級鎖:將 Monitor 的「Owner」設(shè)為 null,喚醒 EntryList 中的線程。

4. 內(nèi)存語義的實現(xiàn)

synchronized 除了互斥,還能保證可見性和有序性,底層通過 內(nèi)存屏障(Memory Barrier) 實現(xiàn):

  • 進(jìn)入同步塊時:插入「Load Barrier」,清空工作內(nèi)存,從主內(nèi)存加載最新共享變量(保證可見性);
  • 退出同步塊時:插入「Store Barrier」,將工作內(nèi)存中的修改刷新到主內(nèi)存(保證可見性);
  • 同步塊內(nèi):禁止指令重排序(保證有序性)。

二、ReentrantLock 的實現(xiàn):JUC 層面的 “手動鎖”

ReentrantLock 是 java.util.concurrent.locks 包下的顯式鎖,基于 AQS(AbstractQueuedSynchronizer,抽象隊列同步器) 實現(xiàn),支持公平鎖 / 非公平鎖、可中斷、條件鎖等靈活特性,完全由 Java 代碼實現(xiàn)(無需 JVM 介入)。

1. 核心依賴組件:AQS(鎖的 “骨架”)

AQS 是 JUC 同步工具的基礎(chǔ)(如 ReentrantLock、Semaphore、CountDownLatch),其核心設(shè)計是「狀態(tài)變量 + 同步隊列」,所有鎖的邏輯都圍繞 AQS 擴(kuò)展。

(1)AQS 的核心結(jié)構(gòu)

  • 狀態(tài)變量(state)volatile int state,存儲鎖的狀態(tài):
    • state = 0:鎖未被持有;
    • state > 0:鎖已被持有,值等于「重入次數(shù)」(支持可重入);
  • 同步隊列(CLH 隊列):雙向鏈表,每個節(jié)點(diǎn)是 Node 對象,存儲等待鎖的線程、等待狀態(tài)(如 CANCELLEDWAITING)等;
  • 獨(dú)占線程(exclusiveOwnerThread):存儲當(dāng)前持有鎖的線程(僅用于獨(dú)占鎖,ReentrantLock 是獨(dú)占鎖)。

(2)AQS 的核心模板方法

AQS 定義了一套「模板方法」,子類(如 ReentrantLock 的內(nèi)部類 Sync)只需實現(xiàn)以下抽象方法,即可完成鎖的邏輯:

  • tryAcquire(int arg):嘗試獲取鎖(arg 為獲取鎖的次數(shù),重入時 arg=1);
  • tryRelease(int arg):嘗試釋放鎖(arg 為釋放鎖的次數(shù),重入時 arg=1);
  • isHeldExclusively():判斷當(dāng)前線程是否持有鎖(支持可重入)。

?? 本質(zhì):AQS 封裝了 “隊列管理”“線程阻塞 / 喚醒” 等通用邏輯,子類只需實現(xiàn) “鎖的獲取 / 釋放” 核心邏輯,遵循「模板方法設(shè)計模式」。

2. ReentrantLock 的內(nèi)部結(jié)構(gòu)

ReentrantLock 本身不直接實現(xiàn) AQS,而是通過內(nèi)部類 Sync 繼承 AQS,再由 Sync 的兩個子類實現(xiàn)「公平鎖」和「非公平鎖」:

public class ReentrantLock implements Lock {
    // 核心同步器(繼承 AQS)
    private final Sync sync;
    // 抽象內(nèi)部類:繼承 AQS,定義鎖的基礎(chǔ)邏輯
    abstract static class Sync extends AbstractQueuedSynchronizer {
        abstract void lock(); // 抽象方法,由公平/非公平鎖實現(xiàn)
        // ... 實現(xiàn) tryRelease 等通用方法
    }
    // 非公平鎖實現(xiàn)(默認(rèn))
    static final class NonfairSync extends Sync {
        @Override void lock() { /* 非公平鎖加鎖邏輯 */ }
        @Override protected boolean tryAcquire(int arg) { /* 非公平嘗試獲取鎖 */ }
    }
    // 公平鎖實現(xiàn)
    static final class FairSync extends Sync {
        @Override void lock() { /* 公平鎖加鎖邏輯 */ }
        @Override protected boolean tryAcquire(int arg) { /* 公平嘗試獲取鎖 */ }
    }
    // 構(gòu)造器:默認(rèn)非公平鎖
    public ReentrantLock() { sync = new NonfairSync(); }
    // 構(gòu)造器:指定公平/非公平
    public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
}

3. ReentrantLock 的核心實現(xiàn)流程(以非公平鎖為例)

(1)加鎖流程(lock() 方法)

// NonfairSync 的 lock() 方法
@Override
public void lock() {
    // 第一步:嘗試 CAS 搶鎖(state 從 0 改為 1)
    if (compareAndSetState(0, 1)) {
        // 搶鎖成功,設(shè)置當(dāng)前線程為獨(dú)占線程
        setExclusiveOwnerThread(Thread.currentThread());
    } else {
        // 搶鎖失敗,調(diào)用 AQS 的 acquire() 模板方法
        acquire(1);
    }
}
// AQS 的 acquire() 模板方法(通用邏輯)
public final void acquire(int arg) {
    // 第二步:嘗試獲取鎖(tryAcquire 由 NonfairSync 實現(xiàn))
    if (!tryAcquire(arg) &&
        // 第三步:獲取失敗,將線程封裝為 Node 加入同步隊列
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
        // 第四步:如果需要,中斷當(dāng)前線程(支持可中斷)
        selfInterrupt();
    }
}
// NonfairSync 的 tryAcquire() 方法(核心搶鎖邏輯)
@Override
protected boolean tryAcquire(int arg) {
    return nonfairTryAcquire(arg);
}
// Sync 的 nonfairTryAcquire() 方法(非公平搶鎖細(xì)節(jié))
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState(); // 獲取當(dāng)前鎖狀態(tài)
    // 情況 1:鎖未被持有(state=0),再次嘗試 CAS 搶鎖
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 情況 2:鎖已被當(dāng)前線程持有(可重入),state 加 1
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) throw new Error("Maximum lock count exceeded");
        setState(nextc); // 無需 CAS,當(dāng)前線程已持有鎖,線程安全
        return true;
    }
    // 情況 3:鎖被其他線程持有,搶鎖失敗
    return false;
}

加鎖核心步驟總結(jié)

  1. 線程先嘗試「CAS 搶鎖」(插隊,非公平鎖的核心);
  2. 搶鎖成功:設(shè)置 state=1,標(biāo)記當(dāng)前線程為獨(dú)占線程;
  3. 搶鎖失?。?ul>
  4. 若當(dāng)前線程已持有鎖(重入):state += 1,直接成功;
  5. 若鎖被其他線程持有:將線程封裝為 Node 加入 AQS 同步隊列,調(diào)用 LockSupport.park() 阻塞線程。

(2)解鎖流程(unlock() 方法)

// ReentrantLock 的 unlock() 方法
public void unlock() {
    // 調(diào)用 Sync 的 release() 方法(AQS 模板方法)
    sync.release(1);
}
// AQS 的 release() 模板方法(通用邏輯)
public final boolean release(int arg) {
    // 第一步:嘗試釋放鎖(tryRelease 由 Sync 實現(xiàn))
    if (tryRelease(arg)) {
        // 第二步:釋放成功,喚醒同步隊列中的頭節(jié)點(diǎn)線程
        Node h = head;
        if (h != null && h.waitStatus != 0) {
            unparkSuccessor(h); // 喚醒后繼節(jié)點(diǎn)(LockSupport.unpark())
        }
        return true;
    }
    return false;
}
// Sync 的 tryRelease() 方法(核心釋放邏輯)
@Override
protected boolean tryRelease(int releases) {
    int c = getState() - releases; // state 減 1(重入時多次減)
    // 只有持有鎖的線程能釋放(否則拋異常)
    if (Thread.currentThread() != getExclusiveOwnerThread()) {
        throw new IllegalMonitorStateException();
    }
    boolean free = false;
    // 情況 1:state 減為 0,完全釋放鎖
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null); // 清空獨(dú)占線程
    }
    // 情況 2:state > 0(重入未完全釋放),僅更新 state
    setState(c);
    return free;
}

解鎖核心步驟總結(jié)

  1. 線程調(diào)用 unlock(),state 減 1;
  2. 若 state == 0:完全釋放鎖,清空獨(dú)占線程,喚醒 AQS 同步隊列中的后繼線程;
  3. 若 state > 0:僅更新 state(重入鎖未完全釋放)。

4. 公平鎖與非公平鎖的實現(xiàn)差異

核心差異在 tryAcquire() 方法中,公平鎖會多一步「判斷隊列是否有等待線程」:

// 公平鎖 FairSync 的 tryAcquire() 方法
@Override
protected boolean tryAcquire(int arg) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 關(guān)鍵差異:先判斷同步隊列是否有前驅(qū)線程(hasQueuedPredecessors())
        if (!hasQueuedPredecessors() && compareAndSetState(0, arg)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        // 重入邏輯與非公平鎖一致
        int nextc = c + arg;
        if (nextc < 0) throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
// AQS 的 hasQueuedPredecessors() 方法:判斷隊列是否有等待線程(先到先得)
public final boolean hasQueuedPredecessors() {
    Node t = tail;
    Node h = head;
    Node s;
    // 隊列不為空,且當(dāng)前線程不是隊列的第一個等待線程 → 有前驅(qū)線程
    return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}
  • 非公平鎖:搶鎖時直接 CAS,不關(guān)心隊列是否有等待線程(允許插隊,性能高,但可能導(dǎo)致饑餓);
  • 公平鎖:搶鎖前先檢查隊列是否有前驅(qū)線程,只有隊列空或當(dāng)前線程是第一個時才 CAS(先到先得,無饑餓,但隊列操作有性能開銷)。

5. 可中斷與條件鎖的實現(xiàn)

(1)可中斷鎖(lockInterruptibly()

ReentrantLock 支持線程在等待鎖時被中斷,底層通過 AQS 的 acquireInterruptibly() 實現(xiàn):

  • 線程在同步隊列中阻塞時,若收到中斷信號(Thread.interrupt()),會拋出 InterruptedException 并退出等待;
  • synchronized 不支持可中斷(線程一旦進(jìn)入阻塞,必須等到鎖釋放或程序終止)。

(2)條件鎖(Condition

ReentrantLock 可通過 newCondition() 獲取 Condition 對象,實現(xiàn) “線程等待 / 喚醒” 的精細(xì)化控制(類似 Object.wait()/notify(),但支持多個等待隊列):

  • Condition 的底層是 AQS 的「等待隊列」(每個 Condition 對應(yīng)一個獨(dú)立等待隊列);
  • 調(diào)用 condition.await():線程釋放鎖,進(jìn)入 Condition 的等待隊列,阻塞;
  • 調(diào)用 condition.signal():喚醒 Condition 等待隊列中的一個線程,使其加入 AQS 同步隊列競爭鎖。

三、synchronized與ReentrantLock實現(xiàn)對比

維度synchronizedReentrantLock
實現(xiàn)層面JVM 內(nèi)置(C++ 實現(xiàn),如 HotSpot 的 synchronizer.cppJava 代碼層面(基于 AQS 框架,純 Java 實現(xiàn))
核心依賴對象頭(Mark Word) + Monitor(操作系統(tǒng)互斥量)AQS(狀態(tài)變量 + 同步隊列) + CAS + LockSupport
鎖升級自動升級(無鎖→偏向→輕量→重量)無鎖升級,始終是重量級鎖(但通過 CAS 減少阻塞)
釋放機(jī)制自動釋放(退出同步塊 / 拋異常)手動釋放(必須在 finally 中調(diào)用 unlock()
公平性僅支持非公平鎖支持公平 / 非公平(構(gòu)造器指定)
可中斷性不支持支持(lockInterruptibly()
條件鎖不支持(僅 Object.wait()/notify(),單等待隊列)支持(Condition,多等待隊列)
性能(JDK1.6+)與 ReentrantLock 接近(鎖升級優(yōu)化后)高并發(fā)下略優(yōu)(靈活控制隊列和 CAS)

總結(jié)

  • synchronized:JVM 原生支持,無需手動管理,通過「對象頭 + Monitor + 鎖升級」實現(xiàn),兼顧簡單性和性能,適合大多數(shù)基礎(chǔ)并發(fā)場景;
  • ReentrantLock:基于 AQS 框架,通過「狀態(tài)變量 + 同步隊列 + CAS」實現(xiàn),支持公平鎖、可中斷、條件鎖等靈活特性,適合復(fù)雜并發(fā)場景(如高并發(fā)讀寫、精細(xì)化線程控制)。

兩者的核心都是「通過互斥保證原子性,通過內(nèi)存屏障保證可見性和有序性」,但實現(xiàn)層面的差異導(dǎo)致了功能和性能的區(qū)別 —— 實際開發(fā)中,簡單場景用 synchronized(不易出錯),復(fù)雜場景用 ReentrantLock(靈活可控)。

到此這篇關(guān)于深入理解Java中的synchronized 和 ReentrantLock 底層原理的文章就介紹到這了,更多相關(guān)java synchronized 和 reentrantlock內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring?Boot實現(xiàn)文件上傳的兩種方式總結(jié)

    Spring?Boot實現(xiàn)文件上傳的兩種方式總結(jié)

    應(yīng)用開發(fā)過程中,文件上傳是一個基礎(chǔ)的擴(kuò)展功能,它的目的就是讓大家共享我們上傳的文件資源,下面這篇文章主要給大家總結(jié)介紹了關(guān)于Spring?Boot實現(xiàn)文件上傳的兩種方式,需要的朋友可以參考下
    2023-05-05
  • k8s中java如何設(shè)置jvm堆棧大小不超過request/limit

    k8s中java如何設(shè)置jvm堆棧大小不超過request/limit

    這篇文章主要介紹了k8s中java如何設(shè)置jvm堆棧大小不超過request/limit問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-07-07
  • Java?-jar命令如何運(yùn)行外部依賴JAR包

    Java?-jar命令如何運(yùn)行外部依賴JAR包

    在Java應(yīng)用部署中,java?-jar命令是啟動可執(zhí)行JAR包的標(biāo)準(zhǔn)方式,但當(dāng)應(yīng)用需要依賴外部JAR文件時,直接使用java?-jar會面臨類加載困境,本文就來聊聊該如何解決吧
    2025-06-06
  • SpringBoot?Security的自定義異常處理

    SpringBoot?Security的自定義異常處理

    這篇文章主要介紹了SpringBoot?Security的自定義異常處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 解決IDEA使用springBoot創(chuàng)建項目,lombok標(biāo)注實體類后編譯無報錯,但是運(yùn)行時報錯問題

    解決IDEA使用springBoot創(chuàng)建項目,lombok標(biāo)注實體類后編譯無報錯,但是運(yùn)行時報錯問題

    文章詳細(xì)描述了在使用lombok的@Data注解標(biāo)注實體類時遇到編譯無誤但運(yùn)行時報錯的問題,分析了可能的原因,并提供了解決步驟
    2025-01-01
  • RocketMQ保證消息的有序性的案例分享

    RocketMQ保證消息的有序性的案例分享

    Apache RocketMQ 是一個常用的開源消息中間件,它提供了強(qiáng)大的有序消息處理能力,這里我們會探討 RocketMQ 是如何保證消息的有序性的,包括其設(shè)計原理和相關(guān)的源碼實現(xiàn),需要的朋友可以參考下
    2024-04-04
  • Spring Boot 靜態(tài)資源處理

    Spring Boot 靜態(tài)資源處理

    今天小編就為大家分享一篇關(guān)于Spring Boot 靜態(tài)資源處理,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • 用Java驗證pdf文件的電子章簽名

    用Java驗證pdf文件的電子章簽名

    這篇文章主要介紹了如何用Java驗證pdf文件的電子章簽名,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-12-12
  • Spring循環(huán)依賴的處理方法

    Spring循環(huán)依賴的處理方法

    循環(huán)依賴是指兩個或多個組件之間相互依賴,形成一個閉環(huán),從而導(dǎo)致這些組件無法正確地被初始化或加載,這篇文章主要介紹了Spring循環(huán)依賴的處理,需要的朋友可以參考下
    2023-08-08
  • java application maven項目打自定義zip包實例(推薦)

    java application maven項目打自定義zip包實例(推薦)

    下面小編就為大家?guī)硪黄猨ava application maven項目打自定義zip包實例(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05

最新評論

马山县| 开封市| 玉龙| 临沭县| 黄石市| 织金县| 巴楚县| 武强县| 宿松县| 兰西县| 老河口市| 绍兴市| 张北县| 古浪县| 介休市| 门源| 乐昌市| 卢氏县| 兰西县| 中卫市| 若羌县| 板桥市| 滕州市| 兴和县| 祁东县| 庄浪县| 浦江县| 策勒县| 射阳县| 中方县| 乌苏市| 玉山县| 九龙坡区| 盘锦市| 宁阳县| 滦平县| 丁青县| 含山县| 衢州市| 湘乡市| 屏东县|