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

Java內(nèi)存模型(JMM)開發(fā)應(yīng)用

 更新時(shí)間:2025年11月04日 08:47:48   作者:佛祖讓我來(lái)巡山  
JMM既是一個(gè)規(guī)范也是一個(gè)工具,正確理解和使用JMM可以幫助我們編寫出既正確又高效的多線程程序,接下來(lái)通過(guò)本文給大家介紹Java內(nèi)存模型(JMM)開發(fā)應(yīng)用實(shí)踐記錄,感興趣的朋友跟隨小編一起看看吧

JMM核心內(nèi)容概覽與重要程度評(píng)級(jí)

在學(xué)習(xí)JMM前,我們先了解其核心內(nèi)容體系及重要程度:

內(nèi)容模塊重要程度說(shuō)明
1. JMM基礎(chǔ)概念????理解JMM的出發(fā)點(diǎn)和基本架構(gòu)
- 硬件基礎(chǔ)與并發(fā)挑戰(zhàn)????了解JMM存在的必要性
- 主內(nèi)存與工作內(nèi)存????JMM的核心抽象概念
2. 內(nèi)存間交互操作???JMM的基礎(chǔ)操作定義
3. volatile關(guān)鍵字?????最常用的同步機(jī)制,必須深入掌握
4. synchronized內(nèi)存語(yǔ)義?????理解鎖的內(nèi)存效應(yīng)
5. happens-before規(guī)則?????JMM的理論核心,解決可見性問題的關(guān)鍵
6. 原子性、可見性、有序性?????并發(fā)編程的三大核心問題
7. 安全發(fā)布模式????實(shí)際開發(fā)中的常用技巧
8. final字段語(yǔ)義???特殊但重要的內(nèi)存語(yǔ)義
9. 雙重檢查鎖定問題????經(jīng)典問題的分析與解決方案
10. JMM底層實(shí)現(xiàn)???理解原理,優(yōu)化性能

接下來(lái),我們將按照重要程度,逐一深入講解各個(gè)模塊。

1. JMM是什么?為什么需要JMM?

1.1 JMM的定義與作用

Java內(nèi)存模型(Java Memory Model, JMM) 是Java虛擬機(jī)規(guī)范中定義的一種抽象規(guī)范,用于屏蔽各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,實(shí)現(xiàn)Java程序在各種平臺(tái)下都能達(dá)到一致的內(nèi)存訪問效果。

JMM的核心作用

  • 定義規(guī)則:規(guī)定多線程環(huán)境下變量的訪問方式
  • 提供保證:確保在不同平臺(tái)上內(nèi)存訪問行為的一致性
  • 允許優(yōu)化:在保證正確性的前提下允許編譯器和處理器進(jìn)行優(yōu)化

1.2 為什么需要JMM:硬件層面的挑戰(zhàn)

現(xiàn)代計(jì)算機(jī)系統(tǒng)的多層次存儲(chǔ)架構(gòu)導(dǎo)致了并發(fā)編程的三大核心問題:

public class ConcurrencyProblems {
    private static boolean ready = false;
    private static int number = 0;
    public static void main(String[] args) {
        // 線程1:數(shù)據(jù)準(zhǔn)備
        Thread writer = new Thread(() -> {
            number = 42;    // 操作1:可能被重排序到操作2之后
            ready = true;   // 操作2:可能先執(zhí)行
        });
        // 線程2:數(shù)據(jù)處理
        Thread reader = new Thread(() -> {
            while (!ready) {
                // 等待ready變?yōu)閠rue
                Thread.yield();
            }
            // 可能輸出0而不是42!
            System.out.println("Number: " + number);
        });
        writer.start();
        reader.start();
    }
}

問題根源

  • CPU緩存一致性:多核CPU各有緩存,數(shù)據(jù)更新不同步
  • 指令重排序:編譯器和處理器為優(yōu)化性能重新排序指令
  • 內(nèi)存可見性:一個(gè)線程的修改對(duì)其他線程不可見

2. JMM的核心架構(gòu):主內(nèi)存與工作內(nèi)存

JMM通過(guò)抽象的內(nèi)存模型解決上述問題:

工作內(nèi)存與主內(nèi)存的交互通過(guò)8種原子操作完成

public class MemoryOperations {
    private int sharedValue = 0;
    public void operationExample() {
        // 1. read: 從主內(nèi)存讀取變量到傳輸通道
        // 2. load: 將read得到的值放入工作內(nèi)存的變量副本
        // 相當(dāng)于: int temp = sharedValue; (但這是高級(jí)語(yǔ)言表示)
        // 3. use: 將工作內(nèi)存中的變量傳遞給執(zhí)行引擎
        int result = sharedValue * 2;
        // 4. assign: 將執(zhí)行引擎的結(jié)果賦給工作內(nèi)存中的變量
        sharedValue = result + 1;
        // 5. store: 將工作內(nèi)存中的變量值傳輸?shù)街鲀?nèi)存的傳輸通道
        // 6. write: 將store獲取的值放入主內(nèi)存的變量
        // 7. lock: 將主內(nèi)存變量標(biāo)記為線程獨(dú)占狀態(tài)
        // 8. unlock: 釋放鎖定的變量
    }
}

3. ????? volatile關(guān)鍵字深度解析

3.1 volatile的語(yǔ)義與保證

volatile是JVM提供的最輕量級(jí)的同步機(jī)制,提供兩大保證:

  • 可見性保證:對(duì)volatile變量的寫操作立即對(duì)其他線程可見
  • 禁止重排序:阻止編譯器和處理器對(duì)volatile操作進(jìn)行重排序
public class VolatileExample {
    private volatile boolean flag = false;
    private int value = 0;
    public void writer() {
        value = 42;      // 普通寫操作
        // StoreStore內(nèi)存屏障:禁止上面的普通寫與下面的volatile寫重排序
        flag = true;     // volatile寫操作
        // StoreLoad內(nèi)存屏障:確保volatile寫立即對(duì)其他處理器可見
    }
    public void reader() {
        // LoadLoad內(nèi)存屏障:確保volatile讀之前的所有讀操作已完成
        if (flag) {      // volatile讀操作
            // LoadStore內(nèi)存屏障:確保volatile讀之后的寫操作不會(huì)重排序到讀之前
            System.out.println(value); // 保證看到value = 42
        }
    }
}

3.2 volatile的實(shí)現(xiàn)原理

在硬件層面,volatile通過(guò)內(nèi)存屏障指令實(shí)現(xiàn):

public class VolatileBarrier {
    private volatile int value;
    public void setValue(int newValue) {
        this.value = newValue;
        // 對(duì)應(yīng)x86匯編代碼:
        // mov    %eax,0x10(%rsi)   ; 將newValue存入value的內(nèi)存地址
        // lock addl $0x0,(%rsp)    ; StoreLoad內(nèi)存屏障(mfence指令)
    }
    public int getValue() {
        // volatile讀在x86上不需要特殊指令
        // 因?yàn)閤86的內(nèi)存模型已經(jīng)保證了可見性(TSO模型)
        return value;
    }
}

內(nèi)存屏障類型

  • LoadLoad屏障:禁止讀操作重排序
  • StoreStore屏障:禁止寫操作重排序
  • LoadStore屏障:禁止讀與寫操作重排序
  • StoreLoad屏障:禁止寫與讀操作重排序(最重量級(jí))

3.3 volatile的使用場(chǎng)景與限制

適用場(chǎng)景

  • 狀態(tài)標(biāo)志位
  • 一次性安全發(fā)布
  • 獨(dú)立觀察(independent observation)
  • 開銷較低的讀-寫鎖策略

不適用場(chǎng)景

  • 復(fù)合操作(如i++)
  • 依賴于當(dāng)前值的操作(如value = value + 1)
public class VolatileUsage {
    // 場(chǎng)景1:狀態(tài)標(biāo)志位
    private volatile boolean shutdownRequested;
    public void shutdown() {
        shutdownRequested = true;
    }
    public void doWork() {
        while (!shutdownRequested) {
            // 執(zhí)行工作任務(wù)
        }
    }
    // 場(chǎng)景2:一次性安全發(fā)布
    private volatile Resource resource;
    public Resource getResource() {
        if (resource == null) {
            synchronized(this) {
                if (resource == null) {
                    resource = new Resource(); // 安全發(fā)布
                }
            }
        }
        return resource;
    }
}

4. ????? synchronized的內(nèi)存語(yǔ)義

synchronized不僅提供互斥執(zhí)行,還提供重要的內(nèi)存語(yǔ)義:

public class SynchronizedMemory {
    private int counter = 0;
    private final Object lock = new Object();
    public void increment() {
        synchronized(lock) { 
            // monitorenter指令:
            // 1. 清空工作內(nèi)存
            // 2. 從主內(nèi)存重新加載所有共享變量
            counter++;
            // 臨界區(qū)內(nèi)的操作不會(huì)被重排序到臨界區(qū)外
        } 
        // monitorexit指令:
        // 1. 將工作內(nèi)存中的修改刷新到主內(nèi)存
        // 2. 釋放鎖
    }
    public int getCounter() {
        synchronized(lock) {
            // 獲取鎖會(huì)強(qiáng)制從主內(nèi)存重新讀取變量
            return counter;
        }
    }
}

synchronized的內(nèi)存語(yǔ)義

  • 進(jìn)入同步塊:清空工作內(nèi)存,從主內(nèi)存重新加載變量
  • 退出同步塊:將工作內(nèi)存中的修改刷新到主內(nèi)存
  • 互斥執(zhí)行:確保同一時(shí)刻只有一個(gè)線程執(zhí)行臨界區(qū)代碼

5. ????? happens-before規(guī)則

happens-before是JMM的理論核心,定義了操作之間的可見性關(guān)系。

5.1 happens-before規(guī)則詳解

public class HappensBeforeExample {
    private int x = 0;
    private volatile boolean v = false;
    private int y = 0;
    private final Object lock = new Object();
    public void demo() {
        // 規(guī)則1:程序次序規(guī)則
        x = 1;          // 操作A
        v = true;       // 操作B:A happens-before B
        // 規(guī)則2:volatile變量規(guī)則
        if (v) {        // 操作C:B happens-before C
            y = x;      // 操作D:C happens-before D
        }
        // 規(guī)則3:傳遞性規(guī)則
        // A happens-before B, B happens-before C, C happens-before D
        // 因此 A happens-before D
        // 規(guī)則4:管程鎖定規(guī)則
        synchronized(lock) { // 加鎖E
            x = 2;          // 操作F:E happens-before F
        } // 解鎖G:F happens-before G
        // 規(guī)則5:線程啟動(dòng)規(guī)則
        Thread t = new Thread(() -> {
            System.out.println(x); // 看到x=2
        });
        t.start(); // start() happens-before 線程中的所有操作
        // 規(guī)則6:線程終止規(guī)則
        try {
            t.join(); // 線程中的所有操作 happens-before join()返回
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        // 規(guī)則7:對(duì)象終結(jié)規(guī)則
        // 對(duì)象的構(gòu)造函數(shù)執(zhí)行結(jié)束 happens-before finalize()方法開始
    }
}

5.2 happens-before的數(shù)學(xué)基礎(chǔ)

happens-before關(guān)系是一個(gè)偏序關(guān)系,具有:

  • 自反性:A happens-before A
  • 反對(duì)稱性:如果A happens-before B且B happens-before A,則A=B
  • 傳遞性:如果A happens-before B且B happens-before C,則A happens-before C

6. ????? 原子性、可見性、有序性

這是并發(fā)編程的三大核心問題,JMM為每個(gè)問題提供了解決方案。

6.1 原子性(Atomicity)

原子性是指一個(gè)操作不可中斷,要么全部執(zhí)行成功,要么完全不執(zhí)行。

public class AtomicityExample {
    private int basicType = 0; // 基本類型訪問是原子的
    private long longValue = 0L; // long和double可能非原子(但現(xiàn)代JVM通常保證原子性)
    private volatile boolean flag = false; // volatile保證單個(gè)讀/寫的原子性
    // 復(fù)合操作不是原子的
    public void nonAtomicIncrement() {
        basicType++; // 不是原子操作!分解為read-modify-write三步
    }
    // 保證原子性的方式
    private final AtomicInteger atomicInt = new AtomicInteger(0);
    private final Object lock = new Object();
    private int synchronizedValue = 0;
    public void atomicOperations() {
        // 方式1:使用原子類
        atomicInt.incrementAndGet(); // 原子操作
        // 方式2:使用同步
        synchronized(lock) {
            synchronizedValue++; // 原子操作
        }
        // 方式3:使用volatile變量(僅適用于特定場(chǎng)景)
        flag = true; // 原子操作
    }
}

6.2 可見性(Visibility)

可見性是指當(dāng)一個(gè)線程修改了共享變量的值,其他線程能夠立即得知這個(gè)修改。

public class VisibilityExample {
    private int noVisibility = 0; // 無(wú)可見性保證
    private volatile boolean hasVisibility = false; // volatile保證可見性
    private int synchronizedValue = 0; // synchronized保證可見性
    private final Object lock = new Object();
    public void demonstrate() {
        // 線程1:修改數(shù)據(jù)
        new Thread(() -> {
            noVisibility = 42;
            hasVisibility = true;
            synchronized(lock) {
                synchronizedValue = 100;
            }
        }).start();
        // 線程2:讀取數(shù)據(jù)
        new Thread(() -> {
            // 可能看不到noVisibility的更新
            while (!hasVisibility) {
                // 等待hasVisibility變?yōu)閠rue
            }
            // 保證看到noVisibility = 42(因?yàn)関olatile寫happens-before volatile讀)
            synchronized(lock) {
                // 保證看到synchronizedValue = 100
            }
        }).start();
    }
}

6.3 有序性(Ordering)

有序性是指程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。

public class OrderingExample {
    private int x = 0;
    private int y = 0;
    private volatile boolean ready = false;
    public void orderingDemo() {
        // 線程1:可能被重排序
        new Thread(() -> {
            x = 1;      // 操作1
            y = 2;      // 操作2
            ready = true; // 操作3:volatile寫,阻止重排序
        }).start();
        // 線程2
        new Thread(() -> {
            while (!ready) {
                // 等待
            }
            // 由于volatile的語(yǔ)義,這里保證看到x=1, y=2
            // 不會(huì)出現(xiàn)y=2但x=0的情況
        }).start();
    }
}

7. ???? 安全發(fā)布模式

安全地發(fā)布對(duì)象是并發(fā)編程中的常見需求,JMM提供了多種模式。

public class SafePublication {
    // 方式1:靜態(tài)初始化器(最安全)
    private static final Resource staticResource = new Resource();
    // 方式2:volatile字段
    private volatile Resource volatileResource;
    public void initVolatileResource() {
        volatileResource = new Resource(); // 安全發(fā)布
    }
    // 方式3:final字段
    private final Resource finalResource;
    public SafePublication() {
        this.finalResource = new Resource(); // 安全發(fā)布
    }
    // 方式4:正常鎖保護(hù)
    private Resource guardedResource;
    private final Object lock = new Object();
    public void initGuardedResource() {
        synchronized(lock) {
            if (guardedResource == null) {
                guardedResource = new Resource(); // 安全發(fā)布
            }
        }
    }
    // 方式5:線程安全容器
    private final Map<String, Resource> safeMap 
        = Collections.synchronizedMap(new HashMap<>());
    private final ConcurrentMap<String, Resource> concurrentMap 
        = new ConcurrentHashMap<>();
    public void addToSafeMap(String key) {
        safeMap.put(key, new Resource()); // 安全發(fā)布
    }
}

8. ???? 雙重檢查鎖定(DCL)問題與解決方案

雙重檢查鎖定是一個(gè)經(jīng)典的并發(fā)模式,但存在陷阱。

8.1 錯(cuò)誤的DCL實(shí)現(xiàn)

public class BrokenDCL {
    private static Resource resource; // 沒有volatile!
    public static Resource getInstance() {
        if (resource == null) {                 // 第一次檢查(無(wú)鎖)
            synchronized(BrokenDCL.class) {     // 加鎖
                if (resource == null) {         // 第二次檢查(有鎖)
                    resource = new Resource();  // 問題所在!
                    // 可能發(fā)生的重排序:
                    // 1. 分配內(nèi)存空間
                    // 2. 將引用指向內(nèi)存空間(此時(shí)resource != null)
                    // 3. 初始化對(duì)象(還未執(zhí)行)
                    // 其他線程可能拿到未完全初始化的對(duì)象!
                }
            }
        }
        return resource;
    }
}

8.2 正確的DCL實(shí)現(xiàn)

public class CorrectDCL {
    // 使用volatile禁止重排序
    private static volatile CorrectDCL instance;
    private final int value;
    private final String name;
    private CorrectDCL() {
        this.value = 42;     // 初始化final字段
        this.name = "DCL";   // 初始化普通字段
        // 構(gòu)造函數(shù)執(zhí)行
    }
    public static CorrectDCL getInstance() {
        if (instance == null) {                     // 第一次檢查(無(wú)鎖)
            synchronized(CorrectDCL.class) {        // 加鎖
                if (instance == null) {             // 第二次檢查(有鎖)
                    instance = new CorrectDCL();    // 安全發(fā)布
                    // volatile寫插入內(nèi)存屏障,確保:
                    // 1. 所有初始化操作完成
                    // 2. 初始化結(jié)果對(duì)其他線程立即可見
                }
            }
        }
        return instance;
    }
}

9. ??? final字段的內(nèi)存語(yǔ)義

final字段在并發(fā)編程中有特殊的內(nèi)存語(yǔ)義,提供了安全初始化的保證。

public class FinalFieldExample {
    private final int finalValue; // final字段
    private int normalValue;      // 普通字段
    private volatile boolean ready = false;
    public FinalFieldExample() {
        normalValue = 1;        // 普通字段寫入(可能被重排序)
        finalValue = 42;        // final字段寫入
        // JMM在此隱式插入StoreStore內(nèi)存屏障
        // 確保final字段的初始化不會(huì)被重排序到構(gòu)造函數(shù)之外
        ready = true;           // volatile寫
    }
    public static void reader() {
        FinalFieldExample obj = new FinalFieldExample();
        // 保證看到finalValue的正確值(42)
        // 即使沒有同步,也能看到正確初始化的final字段
        int r1 = obj.finalValue;
        // 可能看到normalValue的默認(rèn)值(0)而不是1
        // 因?yàn)闆]有同步保證
        int r2 = obj.normalValue;
        // 但如果通過(guò)volatile讀看到ready=true
        // 那么也能保證看到所有字段的正確初始化值
        if (obj.ready) {
            // 保證看到finalValue=42和normalValue=1
        }
    }
}

10. JMM在開發(fā)中的實(shí)際應(yīng)用

10.1 性能優(yōu)化建議

  • 減少同步范圍:只在必要時(shí)使用同步
  • 使用volatile代替鎖:當(dāng)只需要可見性保證時(shí)
  • 使用線程局部變量:避免共享,消除同步
  • 使用并發(fā)容器:代替手動(dòng)同步的容器

10.2 常見陷阱與避免方法

public class CommonConcurrencyMistakes {
    // 陷阱1:認(rèn)為volatile保證原子性
    private volatile int count = 0;
    public void unsafeIncrement() {
        count++; // 不是原子操作!
    }
    // 解決方案:使用原子類或同步
    private final AtomicInteger safeCount = new AtomicInteger(0);
    private int synchronizedCount = 0;
    private final Object lock = new Object();
    public void safeIncrement() {
        safeCount.incrementAndGet(); // 方式1:原子類
        synchronized(lock) {         // 方式2:同步
            synchronizedCount++;
        }
    }
    // 陷阱2:誤用雙重檢查鎖定
    // 解決方案:使用volatile修飾實(shí)例變量
    // 陷阱3:依賴線程優(yōu)先級(jí)
    // 解決方案:不要依賴線程優(yōu)先級(jí)進(jìn)行正確性設(shè)計(jì)
    // 陷阱4:在構(gòu)造函數(shù)中啟動(dòng)線程
    public class ProblematicConstructor {
        public ProblematicConstructor() {
            new Thread(() -> {
                // 可能訪問未完全初始化的對(duì)象
            }).start();
        }
    }
}

總結(jié)

Java內(nèi)存模型是Java并發(fā)編程的基石,它通過(guò)定義一系列規(guī)則和happens-before關(guān)系,在多線程環(huán)境中提供了內(nèi)存可見性、原子性和有序性的保證。

關(guān)鍵要點(diǎn)

  • 理解happens-before規(guī)則:這是理解線程間操作可見性的核心
  • 正確使用volatile:了解其適用場(chǎng)景和限制
  • 掌握安全發(fā)布模式:確保對(duì)象在線程間安全共享
  • 避免常見陷阱:識(shí)別并避免常見的并發(fā)編程錯(cuò)誤

JMM既是一個(gè)規(guī)范也是一個(gè)工具,正確理解和使用JMM可以幫助我們編寫出既正確又高效的多線程程序。在實(shí)際開發(fā)中,應(yīng)該優(yōu)先使用java.util.concurrent包提供的高級(jí)并發(fā)工具,它們?cè)诖蠖鄶?shù)情況下都能提供更好的性能和更簡(jiǎn)單的編程模型。

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

相關(guān)文章

  • SpringBoot配置log4j輸出日志的案例講解

    SpringBoot配置log4j輸出日志的案例講解

    這篇文章主要介紹了SpringBoot配置log4j輸出日志的案例講解,springboot框架中默認(rèn)使用logback進(jìn)行日志輸出,當(dāng)然它也可以配置其它的日志框架,需要的朋友可以參考下
    2022-11-11
  • mybatis?selectKey賦值未生效的原因分析

    mybatis?selectKey賦值未生效的原因分析

    這篇文章主要介紹了mybatis?selectKey賦值未生效的原因分析,selectKey 會(huì)將 SELECT LAST_INSERT_ID()的結(jié)果放入到傳入的實(shí)體類的主鍵里面,文中通過(guò)代碼示例給大家講解非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • Java常用流程控制語(yǔ)句實(shí)現(xiàn)原理解析

    Java常用流程控制語(yǔ)句實(shí)現(xiàn)原理解析

    這篇文章主要介紹了Java常用流程控制語(yǔ)句實(shí)現(xiàn)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • SpringBoot自定義maven-plugin插件整合asm代碼插樁

    SpringBoot自定義maven-plugin插件整合asm代碼插樁

    本文主要介紹了SpringBoot自定義maven-plugin插件整合asm代碼插樁,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • java解析xml的4種方式的優(yōu)缺點(diǎn)對(duì)比及實(shí)現(xiàn)詳解

    java解析xml的4種方式的優(yōu)缺點(diǎn)對(duì)比及實(shí)現(xiàn)詳解

    這篇文章主要介紹了java解析xml的4種方式的優(yōu)缺點(diǎn)對(duì)比及實(shí)現(xiàn)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • Java中的Calendar日歷API用法完全解析

    Java中的Calendar日歷API用法完全解析

    今天特別整理了針對(duì)Java中的Calendar日歷API用法完全解析,通過(guò)Calendar API我們可以對(duì)Calendar所提供的時(shí)間日期字段進(jìn)行各種自定義操作,首先還是從Calendar的基礎(chǔ)入手:
    2016-06-06
  • Java annotation元注解原理實(shí)例解析

    Java annotation元注解原理實(shí)例解析

    這篇文章主要介紹了Java annotation元注解原理實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • java8 List<Object>去掉重復(fù)對(duì)象的幾種方法

    java8 List<Object>去掉重復(fù)對(duì)象的幾種方法

    本文主要介紹了java8 List<Object>去掉重復(fù)對(duì)象的幾種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • SpringBoot實(shí)現(xiàn)評(píng)論回復(fù)功能(數(shù)據(jù)庫(kù)設(shè)計(jì))

    SpringBoot實(shí)現(xiàn)評(píng)論回復(fù)功能(數(shù)據(jù)庫(kù)設(shè)計(jì))

    這篇文章主要介紹了SpringBoot實(shí)現(xiàn)評(píng)論回復(fù)功能(數(shù)據(jù)庫(kù)設(shè)計(jì)),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • 帶你玩轉(zhuǎn)Kafka之初步使用

    帶你玩轉(zhuǎn)Kafka之初步使用

    最近開發(fā)的項(xiàng)目中,kafka用的比較多,為了方便梳理,所以記錄一些關(guān)于kafka的文章,這篇文章主要給大家介紹了關(guān)于Kafka初步使用的相關(guān)資料,需要的朋友可以參考下
    2021-11-11

最新評(píng)論

德安县| 温宿县| 永修县| 闻喜县| 东阳市| 东城区| 当雄县| 军事| 安福县| 即墨市| 松阳县| 调兵山市| 满城县| 昭觉县| 鄂伦春自治旗| 微博| 通州市| 洛隆县| 行唐县| 贺兰县| 江北区| 南木林县| 邵东县| 东乡县| 合肥市| 芮城县| 安多县| 吐鲁番市| 平顶山市| 肥西县| 蒙阴县| 常熟市| 永顺县| 昔阳县| 张北县| 孝义市| 木兰县| 新民市| 利川市| 汉寿县| 高雄县|