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

Java重入鎖(ReentrantLock)從入門到源碼深度解析

 更新時間:2026年02月26日 16:11:06   作者:百錦再@新空間創(chuàng)想科技  
本文給大家介紹Java重入鎖(ReentrantLock)從入門到源碼深度解析,通過本文學習可以全方位地認識ReentrantLock,從基本概念到高級特性,從使用方式到源碼剖析,從底層原理到實際應用,感興趣的朋友跟隨小編一起看看吧

引言

在多線程編程的世界里,鎖是最核心的同步工具之一。Java從語言層面提供了synchronized關鍵字來實現(xiàn)線程同步,簡單而有效。然而,隨著并發(fā)需求的復雜化,synchronized的局限性逐漸顯現(xiàn)——它無法響應中斷、無法設置超時、默認非公平且靈活性不足。為了解決這些問題,Java在java.util.concurrent.locks包中提供了ReentrantLock(重入鎖),一個功能更強大、使用更靈活的鎖工具。

本文將帶你全方位地認識ReentrantLock,從基本概念到高級特性,從使用方式到源碼剖析,從底層原理到實際應用。無論你是初學者還是希望深入理解并發(fā)編程的開發(fā)者,相信都能從中獲得啟發(fā)。全文約8500字,建議結合實踐閱讀。

第一部分:重入鎖基礎概念

1.1 什么是重入鎖?

重入鎖(Reentrant Lock),顧名思義,就是支持重入特性的鎖。重入是指:同一個線程在持有鎖的情況下,可以多次獲取同一把鎖而不會被阻塞

舉個例子:如果一個線程已經獲得了某個對象的鎖,當它再次請求該對象的鎖時,會直接成功,而不是死鎖等待。這種機制在遞歸方法調用或嵌套同步塊中至關重要。

public class ReentrantExample {
    private final Object lock = new Object();
    public void methodA() {
        synchronized (lock) {
            // 已經持有鎖
            methodB(); // 再次請求同一把鎖
        }
    }
    public void methodB() {
        synchronized (lock) {
            // 這里不會死鎖,因為synchronized是可重入的
            System.out.println("methodB執(zhí)行");
        }
    }
}

ReentrantLock同樣支持這種重入特性,但它提供了比synchronized更豐富的功能。

1.2 為什么需要重入鎖?

synchronized作為Java內置的關鍵字,使用簡單,由JVM自動加鎖和解鎖,且經過多年的優(yōu)化(偏向鎖、輕量級鎖、重量級鎖升級),性能已經不遜色于ReentrantLock。既然如此,為什么還需要ReentrantLock?

這是因為ReentrantLock彌補了synchronized的幾個功能性缺陷

特性synchronizedReentrantLock
使用方式關鍵字,自動釋放API調用,需手動釋放
鎖獲取響應中斷不支持支持(lockInterruptibly()
嘗試獲取鎖不支持支持(tryLock()
超時獲取鎖不支持支持(tryLock(long, TimeUnit)
公平鎖非公平可設置公平/非公平
條件變量每個對象一個等待集一個鎖可綁定多個Condition
獲取鎖狀態(tài)無法得知可查詢持有線程、等待隊列等

簡單來說,當需要更精細的控制同步行為時,ReentrantLock是更好的選擇

1.3 ReentrantLock的基本用法

在深入原理之前,我們先來看看ReentrantLock的標準使用模式:

import java.util.concurrent.locks.ReentrantLock;
public class Counter {
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;
    public void increment() {
        lock.lock(); // 獲取鎖
        try {
            count++;
        } finally {
            lock.unlock(); // 必須在finally中釋放鎖!
        }
    }
    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

核心要點

  • lock()unlock()必須成對出現(xiàn)
  • 解鎖操作必須放在finally塊中,確保無論是否發(fā)生異常都能釋放鎖
  • 不能在try塊中調用lock(),因為lock()本身可能拋出異常

第二部分:ReentrantLock的核心特性

2.1 可重入性

可重入性是ReentrantLock命名中的核心特性。它通過計數(shù)機制實現(xiàn):

public class ReentrantDemo {
    private final ReentrantLock lock = new ReentrantLock();
    public void outer() {
        lock.lock();
        try {
            System.out.println("外層方法獲取鎖");
            inner();
        } finally {
            lock.unlock();
        }
    }
    public void inner() {
        lock.lock(); // 同一線程再次獲取鎖
        try {
            System.out.println("內層方法再次獲取鎖");
        } finally {
            lock.unlock();
        }
    }
}

內部原理:每個鎖關聯(lián)一個持有線程和一個計數(shù)器。當線程第一次獲取鎖時,計數(shù)器置為1;同一個線程再次獲取鎖時,計數(shù)器遞增;每釋放一次,計數(shù)器遞減;當計數(shù)器歸零時,鎖完全釋放,其他線程才能獲取。

2.2 公平鎖與非公平鎖

2.2.1 概念解析

  • 公平鎖(FairSync):線程按照請求鎖的先后順序(FIFO)獲取鎖,不會產生饑餓現(xiàn)象。
  • 非公平鎖(NonfairSync):線程在獲取鎖時,允許"插隊",即直接嘗試搶占鎖,如果搶占成功就直接獲得鎖,搶占失敗才進入隊列等待。

ReentrantLock默認使用非公平鎖,但可以通過構造器參數(shù)設置為公平鎖:

ReentrantLock fairLock = new ReentrantLock(true);   // 公平鎖
ReentrantLock unfairLock = new ReentrantLock(false); // 非公平鎖
ReentrantLock defaultLock = new ReentrantLock();    // 默認非公平鎖

2.2.2 為什么默認非公平鎖?

非公平鎖雖然可能導致線程饑餓,但性能更高。原因在于:

  • 公平鎖需要維護嚴格的排隊機制,線程喚醒有開銷
  • 非公平鎖減少了線程的掛起和喚醒次數(shù)
  • 在高并發(fā)場景下,非公平鎖的吞吐量通常優(yōu)于公平鎖

2.2.3 源碼層面的差異

我們來看看非公平鎖的lock()方法:

// NonfairSync的lock方法
final void lock() {
    // 直接嘗試搶占鎖(插隊)
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

而公平鎖的lock()方法:

// FairSync的lock方法
final void lock() {
    acquire(1); // 直接進入隊列,沒有搶占機會
}

公平鎖的tryAcquire方法中多了一個關鍵判斷:

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 公平鎖的額外判斷:隊列中是否有前驅節(jié)點
        if (!hasQueuedPredecessors() && 
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // ... 重入邏輯
    return false;
}

hasQueuedPredecessors()檢查隊列中是否有等待時間更長的線程,確保嚴格FIFO。

2.3 可中斷鎖

synchronized在等待鎖的過程中無法響應中斷,而ReentrantLock提供了可中斷的獲取鎖方式:

public class InterruptibleDemo {
    private final ReentrantLock lock = new ReentrantLock();
    public void performTask() throws InterruptedException {
        // 可響應中斷的鎖獲取
        lock.lockInterruptibly();
        try {
            // 執(zhí)行需要同步的操作
            System.out.println(Thread.currentThread().getName() + " 獲得鎖");
            Thread.sleep(5000);
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) throws Exception {
        InterruptibleDemo demo = new InterruptibleDemo();
        Thread t1 = new Thread(() -> {
            try {
                demo.performTask();
            } catch (InterruptedException e) {
                System.out.println("線程1被中斷");
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                demo.performTask();
            } catch (InterruptedException e) {
                System.out.println("線程2被中斷");
            }
        });
        t1.start();
        Thread.sleep(100); // 確保t1先獲得鎖
        t2.start();
        // 中斷正在等待鎖的t2
        t2.interrupt();
    }
}

當t2在等待鎖時被中斷,會立即拋出InterruptedException,從而有機會響應中斷,而不是無限阻塞。

2.4 限時等待鎖

在實際開發(fā)中,無限等待鎖可能導致系統(tǒng)死鎖或響應延遲。ReentrantLock提供了帶超時的鎖獲取方法:

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TimeoutDemo {
    private final ReentrantLock lock = new ReentrantLock();
    public boolean tryExecute() {
        try {
            // 嘗試在3秒內獲取鎖
            if (lock.tryLock(3, TimeUnit.SECONDS)) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 獲得鎖");
                    Thread.sleep(2000); // 模擬業(yè)務操作
                    return true;
                } finally {
                    lock.unlock();
                }
            } else {
                System.out.println(Thread.currentThread().getName() + " 獲取鎖超時");
                return false;
            }
        } catch (InterruptedException e) {
            System.out.println("線程被中斷");
            return false;
        }
    }
}

tryLock()還有無參版本:如果鎖可用則立即獲取,否則立即返回false,不會阻塞。

2.5 條件變量(Condition)

ConditionObjectwait()notify()、notifyAll()方法分解為不同的條件對象,使得一個鎖可以支持多個等待集,實現(xiàn)更精細的線程協(xié)作。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class BoundedBuffer {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    private final Object[] items = new Object[10];
    private int putIndex, takeIndex, count;
    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            // 當隊列滿時,等待notFull條件
            while (count == items.length) {
                notFull.await(); // 釋放鎖,進入等待
            }
            items[putIndex] = x;
            if (++putIndex == items.length) putIndex = 0;
            count++;
            // 通知等待notEmpty條件的線程
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            // 當隊列空時,等待notEmpty條件
            while (count == 0) {
                notEmpty.await();
            }
            Object x = items[takeIndex];
            if (++takeIndex == items.length) takeIndex = 0;
            count--;
            // 通知等待notFull條件的線程
            notFull.signal();
            return x;
        } finally {
            lock.unlock();
        }
    }
}

優(yōu)勢:與synchronized相比,Condition可以創(chuàng)建多個等待集,更靈活地控制線程協(xié)作。

第三部分:ReentrantLock與synchronized的全面對比

3.1 異同點總結

比較維度synchronizedReentrantLock
實現(xiàn)方式JVM內置關鍵字Java API實現(xiàn),基于AQS
鎖釋放自動釋放(退出同步塊)手動釋放(需finally中unlock)
可重入性支持支持
公平性非公平可公平可非公平
響應中斷不支持支持(lockInterruptibly)
超時獲取不支持支持(tryLock帶超時)
嘗試獲取不支持支持(tryLock無參)
條件變量每個對象一個等待集一個鎖可多個Condition
鎖狀態(tài)查詢無法查詢可查詢持有線程、等待隊列長度等
性能JDK6后優(yōu)化良好高競爭場景表現(xiàn)更優(yōu)

3.2 如何選擇?

根據(jù)實際場景選擇:

  • 優(yōu)先使用synchronized:當同步邏輯簡單、不需要高級特性時。它簡潔、不易出錯,且JVM持續(xù)優(yōu)化。
  • 需要公平鎖:必須保證線程獲取鎖的順序時。
  • 需要可中斷鎖:希望避免線程無限期阻塞時。
  • 需要超時獲取鎖:防止死鎖或保證響應時間時。
  • 需要多個條件變量:生產者-消費者模式等復雜協(xié)作時。
  • 高競爭場景ReentrantLock在高并發(fā)下表現(xiàn)更好。

第四部分:ReentrantLock源碼深度剖析

4.1 AQS基礎:重入鎖的基石

要理解ReentrantLock,必須先理解AQS(AbstractQueuedSynchronizer)。AQS是Java并發(fā)包的基石,ReentrantLock、Semaphore、CountDownLatch等工具都基于它實現(xiàn)。

4.1.1 AQS的核心思想

AQS維護了兩個核心元素:

  1. volatile int state:同步狀態(tài),對于ReentrantLock,state表示鎖的持有次數(shù)(0表示未持有,≥1表示持有次數(shù))。
  2. FIFO等待隊列(CLH隊列變體):用于存放獲取鎖失敗的線程。

核心操作:通過CAS(Compare And Swap)原子性地修改state值,成功則獲得鎖,失敗則進入等待隊列。

4.1.2 AQS的關鍵方法

方法描述
tryAcquire(int arg)嘗試獲取鎖,由子類實現(xiàn)
tryRelease(int arg)嘗試釋放鎖,由子類實現(xiàn)
acquire(int arg)獲取鎖的模板方法
release(int arg)釋放鎖的模板方法

ReentrantLock內部類Sync繼承AQS,并實現(xiàn)了tryAcquiretryRelease。

4.2 非公平鎖源碼解析

4.2.1 加鎖過程

// ReentrantLock.NonfairSync
final void lock() {
    // 第一步:直接嘗試搶占鎖(插隊)
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1); // 搶占失敗,進入AQS流程
}
  • compareAndSetState(0, 1):通過CAS嘗試將state從0改為1。如果成功,表示當前線程直接搶到了鎖,設置獨占線程為當前線程。
  • 如果CAS失?。ㄦi已被其他線程持有),調用acquire(1)進入AQS的獲取流程。
// AbstractQueuedSynchronizer.acquire
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&          // 再次嘗試獲?。ǚ枪桨妫?
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 加入等待隊列
        selfInterrupt();
}

這里的tryAcquire調用的是NonfairSync實現(xiàn)的nonfairTryAcquire

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // state為0,說明鎖空閑,再次嘗試CAS搶占
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        // 重入:同一線程再次獲取鎖
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

非公平的體現(xiàn):即使線程已經進入等待隊列,在tryAcquire階段仍然會嘗試CAS搶占,而不是嚴格排隊。

4.2.2 入隊等待

如果tryAcquire失敗,則執(zhí)行addWaiter將當前線程封裝成Node加入等待隊列尾部:

private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node); // 入隊失敗或有并發(fā)時,通過自旋CAS入隊
    return node;
}

然后執(zhí)行acquireQueued,在隊列中自旋等待:

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            // 如果前驅是頭節(jié)點,再次嘗試獲取鎖
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 檢查是否需要掛起線程
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

當線程獲取鎖失敗時,會被park(掛起),等待前驅線程釋放鎖時unpark喚醒。

4.2.3 釋放鎖

// ReentrantLock.Sync
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

釋放鎖時遞減state,直到state歸零才真正釋放鎖。然后AQS會喚醒隊列中的下一個節(jié)點。

4.3 公平鎖源碼解析

公平鎖的lock()方法直接調用acquire(1),沒有搶占嘗試:

// FairSync.lock
final void lock() {
    acquire(1);
}

公平鎖的tryAcquire與非公平鎖的核心區(qū)別在于多了hasQueuedPredecessors()判斷:

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 公平鎖:檢查隊列中是否有前驅節(jié)點
        if (!hasQueuedPredecessors() && 
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // ... 重入邏輯
    return false;
}

hasQueuedPredecessors()判斷當前線程之前是否有等待的線程,確保FIFO順序。

4.4 限時獲取鎖的實現(xiàn)

tryLock(long timeout, TimeUnit unit)的底層通過doAcquireNanos實現(xiàn):

private boolean doAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    long lastTime = System.nanoTime();
    final Node node = addWaiter(Node.EXCLUSIVE);
    // ...
    for (;;) {
        // 嘗試獲取鎖
        // 計算剩余時間,超時則返回false
        nanosTimeout -= System.nanoTime() - lastTime;
        if (nanosTimeout <= 0) {
            cancelAcquire(node);
            return false;
        }
        // 如果超時時間短,自旋;否則掛起
        if (shouldParkAfterFailedAcquire(p, node) &&
            nanosTimeout > spinForTimeoutThreshold)
            LockSupport.parkNanos(this, nanosTimeout);
        // ...
    }
}

通過LockSupport.parkNanos實現(xiàn)限時阻塞,超時后自動喚醒并返回失敗。

第五部分:CAS與AQS——重入鎖的底層基石

5.1 CAS操作

CAS(Compare And Swap)是并發(fā)編程中實現(xiàn)無鎖算法的核心技術。它是一條CPU原子指令,包含三個操作數(shù):

  • 內存地址V
  • 期望值A
  • 新值B

僅當V的值等于A時,才將V更新為B,整個過程原子完成。

在Java中,Unsafe類提供了CAS操作,ReentrantLock通過CAS修改AQS的state字段。

5.2 CAS的ABA問題

ABA問題:線程1讀取變量值為A,此時線程2將A改為B再改回A,線程1CAS時發(fā)現(xiàn)仍是A,于是更新成功。但實際上變量已經被修改過。

解決方案:使用版本號或時間戳。Java提供了AtomicStampedReference來解決ABA問題。

5.3 AQS的設計精髓

AQS的核心設計理念包括:

  1. 模板方法模式:定義獲取/釋放鎖的骨架,具體實現(xiàn)由子類完成。
  2. CLH隊列變體:高效的雙向隊列管理等待線程。
  3. 狀態(tài)依賴:通過state表示同步狀態(tài)。
  4. 自旋與阻塞結合:短時間內自旋,長時間阻塞,平衡性能。
  5. LockSupport:提供線程掛起和喚醒的底層支持。

第六部分:實戰(zhàn)應用與最佳實踐

6.1 標準使用模板

public class SafeCounter {
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
    // 帶超時的獲取
    public boolean tryIncrement(long timeout, TimeUnit unit) {
        try {
            if (lock.tryLock(timeout, unit)) {
                try {
                    count++;
                    return true;
                } finally {
                    lock.unlock();
                }
            }
            return false;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        }
    }
}

6.2 監(jiān)控與調試

ReentrantLock提供了監(jiān)控鎖狀態(tài)的方法:

ReentrantLock lock = new ReentrantLock();
// 查詢鎖狀態(tài)
System.out.println("鎖持有線程: " + lock.getOwner());
System.out.println("等待線程數(shù): " + lock.getQueueLength());
System.out.println("是否被當前線程持有: " + lock.isHeldByCurrentThread());
System.out.println("是否公平鎖: " + lock.isFair());

這些方法對調試死鎖、監(jiān)控系統(tǒng)狀態(tài)非常有幫助。

6.3 常見陷阱與注意事項

  1. 忘記釋放鎖:必須在finally中unlock。
  2. 在try塊內lock:lock()可能拋出異常,應該先lock再try。
  3. 鎖的可見性問題:ReentrantLock保證內存可見性,無需額外volatile。
  4. 重入計數(shù)溢出:重入次數(shù)受int范圍限制,理論上可達21億次。
  5. 與synchronized混用:不同鎖機制之間不互斥,需注意設計。

6.4 性能考量

  • 低競爭場景:synchronized性能略優(yōu)或持平
  • 高競爭場景:ReentrantLock性能更好
  • 公平鎖性能低于非公平鎖
  • 避免在鎖內執(zhí)行耗時操作

結語

ReentrantLock作為Java并發(fā)包中的核心工具,以其強大的功能和靈活的機制,成為高并發(fā)編程中不可或缺的利器。通過本文的學習,我們深入理解了:

  • 可重入性的實現(xiàn)原理
  • 公平鎖與非公平鎖的源碼差異
  • 可中斷、限時等待等高級特性
  • AQS作為底層的核心架構
  • 最佳實踐與性能考量

掌握ReentrantLock不僅僅是學會使用一個類,更是理解Java并發(fā)編程思想的重要一步。在實際開發(fā)中,根據(jù)場景選擇合適的同步工具,平衡功能與性能,才能寫出高質量的多線程程序。

到此這篇關于Java重入鎖(ReentrantLock)全面解析:從入門到源碼深度剖析的文章就介紹到這了,更多相關Java重入鎖ReentrantLock內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 淺談SpringBoot之事務處理機制

    淺談SpringBoot之事務處理機制

    這篇文章主要介紹了淺談SpringBoot之事務處理機制,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • 搭建MyBatis開發(fā)環(huán)境及基本的CURD介紹

    搭建MyBatis開發(fā)環(huán)境及基本的CURD介紹

    這篇文章主要介紹了搭建MyBatis開發(fā)環(huán)境及基本的CURD,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-08-08
  • Java的HashSet源碼詳解

    Java的HashSet源碼詳解

    這篇文章主要介紹了Java的HashSet源碼詳解,HashSet底層封裝的是HashMap,所以元素添加會放到HashMap的key中,value值使用new Object對象作為value,所以HashSet和HashMap的所具有的特點是類似的,需要的朋友可以參考下
    2023-09-09
  • springboot 2.3之后消失的hibernate-validator解決方法

    springboot 2.3之后消失的hibernate-validator解決方法

    這篇文章主要介紹了springboot 2.3之后消失的hibernate-validator解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08
  • Mybatis Plus Wrapper查詢某幾列的方法實現(xiàn)

    Mybatis Plus Wrapper查詢某幾列的方法實現(xiàn)

    MybatisPlus中,使用Wrapper的select和notSelect方法可以精確控制查詢的字段,本文就來介紹一下Mybatis Plus Wrapper查詢某幾列的方法實現(xiàn),感興趣的可以了解一下
    2024-10-10
  • 使用IntelliJ IDEA調式Stream流的方法步驟

    使用IntelliJ IDEA調式Stream流的方法步驟

    本文主要介紹了使用IntelliJ IDEA調式Stream流的方法步驟,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Java字符串拼接詳解

    Java字符串拼接詳解

    大家好,本篇文章主要講的是Java字符串拼接詳解,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-02-02
  • spring?cloud?eureka注冊原理-注冊失敗填坑筆記

    spring?cloud?eureka注冊原理-注冊失敗填坑筆記

    這篇文章主要介紹了spring?cloud?eureka注冊原理-注冊失敗填坑筆記,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • 將一個數(shù)組按照固定大小進行拆分成數(shù)組的方法

    將一個數(shù)組按照固定大小進行拆分成數(shù)組的方法

    下面小編就為大家?guī)硪黄獙⒁粋€數(shù)組按照固定大小進行拆分成數(shù)組的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-11-11
  • SpringBoot內嵌tomcat處理有特殊字符轉義的問題

    SpringBoot內嵌tomcat處理有特殊字符轉義的問題

    這篇文章主要介紹了SpringBoot內嵌tomcat處理有特殊字符轉義的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06

最新評論

东平县| 芦溪县| 彩票| 乌鲁木齐县| 柳州市| 永州市| 句容市| 双江| 当雄县| 陆川县| 泰和县| 班戈县| 日喀则市| 花垣县| 德阳市| 三河市| 长汀县| 章丘市| 阳信县| 图木舒克市| 泸西县| 错那县| 南郑县| 常宁市| 独山县| 明光市| 金坛市| 横山县| 南京市| 浦江县| 江城| 女性| 南投市| 钦州市| 宁德市| 水城县| 南涧| 湘潭县| 华坪县| 平罗县| 丹棱县|