Java EE初階——線程安全

1.?線程的狀態

1. 線程狀態分類(Thread.State?枚舉)

Java 定義了 6 種線程狀態,這些狀態均由?java.lang.Thread.State?枚舉表示:

  1. NEW(新建)
    線程對象已創建,但尚未調用?start()?方法。此時線程尚未啟動,只是一個普通的 Java 對象。

  2. RUNNABLE(可運行)
    線程已調用?start()?方法,正在 JVM 中運行。該狀態包含兩種實際情況:

    • READY(就緒):線程已獲取除 CPU 外的所有資源,等待操作系統調度。
    • RUNNING(運行中):線程正在 CPU 上執行。
  3. BLOCKED(阻塞)
    線程因等待監視器鎖(如進入?synchronized?塊 / 方法)而被阻塞。線程會在獲取鎖后恢復為 RUNNABLE 狀態。

  4. WAITING(無限期等待)
    線程因調用以下方法而進入無限期等待狀態,必須等待其他線程顯式喚醒:

    • Object.wait()
    • Thread.join()
    • LockSupport.park()
    • 喚醒條件

      • notify()/notifyAll()

      • 目標線程終止(針對?join()

  5. TIMED_WAITING(限期等待)
    線程因調用以下帶超時參數的方法而進入限期等待狀態,超時后自動喚醒:

    • Thread.sleep(long millis)
    • Object.wait(long timeout)
    • Thread.join(long millis)
    • LockSupport.parkNanos()
    • LockSupport.parkUntil()
  6. TERMINATED(終止)
    線程執行完畢(run()?方法正常退出)或因異常終止,線程生命周期結束。

2. 線程的狀態和轉移

1.? NEW RUNNABLE TERMINATED 狀態的轉換
public class ThreadDomo1 {public static void main(String[] args) {Thread t = new Thread(()->{for(int i=0;i<1;i++){}});System.out.println(t.getState());//NEWt.start();while(t.isAlive()){//線程存活System.out.println(t.getState());//RUNNABLE}System.out.println(t.getState());//TERMINATED}
}
  1. 線程狀態不可逆:線程一旦進入 TERMINATED 狀態,無法再次啟動(調用?start()?會拋出?IllegalThreadStateException)。
  2. BLOCKED 與 WAITING 的區別
    • BLOCKED 是因等待監視器鎖而阻塞。
    • WAITING/TIMED_WAITING 是主動調用方法進入等待狀態,需顯式喚醒或超時。

2. 線程安全

Java 標準庫中的線程安全類

1. Java 標準庫中很多都是線程不安全的. 這些類可能會涉及到多線程修改共享數據, ?沒有任何加鎖措施.
? ArrayList
? LinkedList
? HashMap
? TreeMap
? HashSet
? TreeSet
? StringBuilder
2. 但是還有?些是線程安全的. 使?了?些鎖機制來控制.
? Vector (不推薦使?)
? HashTable (不推薦使?)
? ConcurrentHashMap
? StringBuffer

3. 還有的雖然沒有加鎖, 但是不涉及 "修改", 仍然是線程安全的
? String

線程安全是多線程編程中的核心概念,指的是在多線程環境下,程序的行為和結果與單線程環境下一致,不會出現數據競爭、不一致或其他意外情況。

如果這個代碼在單線程環境下運行正確,在多線程環境下產生 bug ,這種情況就稱為“線程不安全”或“存在線程安全問題”?。

public class ThreadDomo2 {public static int count = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for(int i=0;i<50000;i++){count++;}});Thread t2 = new Thread(()->{for(int i=0;i<50000;i++){count++;}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);//預期結果 10w}
}

運行代碼發現,結果每次都不一樣,結果大概在5w-10w之間,這是為什么?

1. 線程不安全原因

1. 【根本原因】線程調度是隨機的

操作系統上的線程是“搶占式執行” “隨機調度”?

2.?修改共享數據

代碼結構:進程中多個線程同時修改同一個變量

? 沒有問題:1. 一個線程修改一個變量

? ? ? ? ? ? ? ? ? ? 2. 多個線程讀取同一個變量

? ? ? ? ? ? ? ? ? ? 3. 多個線程修改不同變量

3. 【直接原因】原子性

多線程同時修改同一個變量操作不是原子操作

count++; 由三個指令構成:

? 1. load? 從內存中讀取數據到 cpu 寄存器

? 2. add 把寄存器數值 +1

? 3. save 把寄存器的值寫回到 內存 中

t1 和 t2 是并發執行的,可能交錯執行這三步,導致部分增量丟失。

1,2 為線程安全,其余都為線程不安全

關鍵在于,要確保前一個線程 save 之后,第二個線程再 load ,否則第二個線程 load 到的結果就是第一個線程自增前的結果,兩次自增就只 +1

即一個線程執行?1-n(基本為1次)這自增,被另一個線程覆蓋成自增 1 次的情況。

4. 可見性

可?性指?個線程對共享變量值的修改,能夠及時地被其他線程看到.
Java 內存模型 (JMM): Java虛擬機規范中定義了Java內存模型.
?的是屏蔽掉各種硬件和操作系統的內存訪問差異,以實現讓Java程序在各種平臺下都能達到?致的并發效果.

? 線程之間的共享變量存在 主內存 (Main Memory).
? 每?個線程都有??的 "?作內存" (Working Memory) .
? 當線程要讀取?個共享變量的時候, 會先把變量從主內存拷?到?作內存, 再從?作內存讀取數據.
? 當線程要修改?個共享變量的時候, 也會先修改?作內存中的副本, 再同步回主內存.
所謂的 "主內存" 才是真正硬件?度的 "內存". ?所謂的 "?作內存", 則是指 CPU 的寄存器和?速緩存.
CPU 訪問??寄存器的速度以及?速緩存的速度, 遠遠超過訪問內存的速度(快了 3 - 4 個數量級, 也就是?千倍, 上萬倍).
import java.util.Scanner;public class ThreadDomo3 {public static int flg = 1;//使用 volatile 關鍵字//public static volatile int flg = 1;public static void main(String[] args) throws InterruptedException {Scanner scan = new Scanner(System.in);Thread t1 = new Thread(()->{while(flg==1){ // 循環檢查 flg 的值// 空循環,等待 flg 變為非 1}System.out.println("t1 線程結束");});Thread t2 = new Thread(()->{System.out.print("請輸入flg的值:");flg = scan.nextInt();// 從控制臺讀取輸入,修改 flg 的值System.out.println("t2 線程結束");});t1.start();t2.start();}
}

? ?

使用 volatile 關鍵字

在該代碼中,我們預期是通過 t2 線程輸入一個非 1 數,使 t1 線程結束,事實卻是我們輸入非 1 整數,t1 線程并未結束,這是為什么?

while(flg == 1);核心指令有兩條:

1. load 讀取內存中?flg 的值到cpu寄存器中

2. 拿寄存器中獲取的值與1進行比較(條件跳轉指令)

頻繁執行 load 操作和條件跳轉,load 操作執行的結果,每次都是一樣的,且 load 操作開銷遠遠高于條件跳轉,訪問寄存器的操作速度遠遠超過訪問內存,此時 jvm 就可能做出代碼優化,優化掉 load 操作,以提高循環的執行速度。卻導致 t2 線程對共享變量的修改無法及時被 t1 線程看到。造成線程不安全。

這種優化被稱為?循環不變代碼外提(Loop Invariant Code Motion),它將循環內不變的操作(如?load)移到循環外,大幅提高執行效率。但在多線程環境下,這種優化會導致?內存可見性問題:即使其他線程修改了?flg?的值,執行優化后的線程仍使用寄存器中的舊值。

volatile 關鍵字

volatile?是一個用于修飾變量的關鍵字,主要用于保證變量的內存可見性禁止指令重排序

保證可見性
每次訪問變量必須要重新讀取內存,而不會優化到寄存器/緩存中
代碼在寫? volatile 修飾的變量的時候,
? ?? 改變線程?作內存中volatile變量副本的值
? ?? 將改變后的副本的值從?作內存刷新到主內存
代碼在讀取 volatile 修飾的變量的時候
? ?? 從主內存中讀取volatile變量的最新值到線程的?作內存中
? ?? 從?作內存中讀取volatile變量的副本

禁止指令重排序

針對被 volatile 修飾的變量必須都要重新讀取內存,而不會優化到寄存器/緩存中

  • 原理volatile?變量會插入內存屏障(Memory Barrier),禁止編譯器和處理器對指令進行重排序

5. 指令重排序

  • 編譯器重排序:編譯器為優化性能,可能改變代碼的執行順序。
  • 處理器重排序:處理器為提高指令執行效率,可能對指令進行亂序執行。

2.?synchronized 關鍵字 - 監視器鎖 monitor lock

針對線程不安全原因3,使用 加鎖 的方式,將非原子指令打包成一個整體,確保同一時間,只有該線程可以執行此非原子指令。

synchronized?關鍵字是實現線程同步的核心機制之一,它基于 ** 監視器鎖(Monitor Lock)** 來保證同一時間只有一個線程可以執行被保護的代碼塊或方法

1、監視器鎖的底層原理

  1. 每個對象都有一個監視器鎖
    Java 中每個對象(包括類實例和數組)都關聯著一個監視器鎖(也稱為內部鎖或互斥鎖)。當一個線程試圖訪問被?synchronized?保護的代碼時,它必須先獲取該對象的監視器鎖。

  2. 鎖的獲取與釋放

    • 獲取鎖:線程進入?synchronized?代碼塊前,必須獲取對象的監視器鎖。如果鎖已被其他線程持有,則當前線程會被阻塞,進入鎖的等待隊列。
    • 釋放鎖:線程退出?synchronized?代碼塊時,會自動釋放監視器鎖,喚醒等待隊列中的其他線程競爭鎖。
  3. JVM 實現
    監視器鎖的實現依賴于對象頭中的?Mark Word。當對象被鎖定時,Mark Word 會存儲指向鎖記錄的指針,不同狀態(偏向鎖、輕量級鎖、重量級鎖)下的存儲結構不同

2.?synchronized?的使用方式

1.?同步實例方法(鎖對象為 this)

使用當前對象實例(this)作為鎖

直接修飾普通?法

public class SynchronizedDomo {public synchronized void method(){//...}
}
  • 鎖對象:隱式使用當前對象實例(this)。
  • 作用范圍:整個方法體。
  • 字節碼層面:JVM 使用?ACC_SYNCHRONIZED?標志來標記方法,當線程調用該方法時,會自動獲取鎖并在方法退出時釋放鎖。

同步代碼塊

public class SynchronizedDomo {public void method() {synchronized (this) {// 同步代碼}}
}
  • 鎖對象:顯式指定為?this(當前對象實例)。
  • 作用范圍:僅包含在?{}?內的代碼。
  • 字節碼層面:使用?monitorenter?和?monitorexit?指令實現鎖的獲取和釋放。
2.?同步靜態方法(鎖對象為類的 Class 對象)

使用類的?Class?對象(即?SynchronizedDomo1.class)作為鎖

synchronized?修飾靜態方法

public class SynchronizedDomo {public static synchronized void method(){//...}
}
  • 鎖對象:隱式使用當前類的?Class?對象(如?SynchronizedDomo1.class)。
  • 作用范圍:整個靜態方法體。
  • 字節碼層面:JVM 使用?ACC_SYNCHRONIZED?標志來標記靜態方法,當線程調用該方法時,會自動獲取類的?Class?對象鎖并在方法退出時釋放鎖。

同步靜態代碼塊

public class SynchronizedDomo {public static void method() {// 反射 類名.class 獲取當前類的 class 對象synchronized (SynchronizedDomo1.class) {// 同步代碼}}
}
  • 鎖對象:顯式指定為當前類的?Class?對象。
  • 作用范圍:僅包含在?{}?內的代碼。
  • 字節碼層面:使用?monitorenter?和?monitorexit?指令實現鎖的獲取和釋放。
3. 同步代碼塊,指定鎖對象(locker)
public class SynchronizedDomo1 {//創建鎖對象(鎖對象可以是任意Object)private Object locker = new Object();public void method(){synchronized (locker){//...}}
}
    代碼實例
    public class ThreadDomo3 {public static int count = 0; //共享變量public static void main(String[] args) throws InterruptedException {//創建對象(任意)作為鎖對象Object locker = new Object();Thread t1 = new Thread(()->{for(int i=0;i<50000;i++){// 使用locker作為鎖,進入同步塊前會獲取鎖synchronized (locker){count++;}// 退出同步塊時自動釋放鎖}});Thread t2 = new Thread(()->{for(int i=0;i<50000;i++){// 使用locker作為鎖,進入同步塊前會獲取鎖synchronized (locker){ count++;} // 退出同步塊時自動釋放鎖//count++;}});// 啟動兩個線程t1.start();t2.start();// 主線程等待兩個線程執行完畢t1.join();t2.join();System.out.println(count);//100000}
    }

    3. 互斥

    • 互斥?指同一時間只允許一個線程訪問共享資源,其他線程必須等待。

    • 通過?鎖(Lock)?或?同步機制(如?synchronized)實現。

    作用

    • 保證原子性:防止多個線程同時修改共享數據導致的數據不一致。
    • 維護可見性:確保一個線程對共享變量的修改能被其他線程正確看到。
    • 防止多個線程同時修改共享數據(如?count++),造成線程不安全

    4. 鎖競爭

    鎖競爭是指多個線程同時嘗試獲取同一把鎖時發生的競爭現象。當鎖被一個線程持有時,其他線程必須等待,從而導致線程阻塞上下文切換,降低程序性能。

    競爭程度表現解決方案
    低競爭線程偶爾阻塞,性能影響小無優化必要
    高競爭大量線程阻塞,CPU空轉減小鎖粒度、無鎖算法

    在上述代碼中,兩個線程訪問共享資源 count,?t1 線程進行了同步保護,t2?線程直接訪問,就不會形成鎖競爭,t2?線程可能看不到?t1?線程對 count?的修改,count ++的原子性被破壞,造成線程不安全。

    ? ???

    5. 可重入

    可重入是指同一個線程可以多次獲取同一把鎖而不會被阻塞可重入鎖會記錄鎖的持有線程和重入次數,當線程退出同步塊時,只有重入次數降為 0 才會真正釋放鎖。

    • 實現原理

      • synchronized?通過?鎖計數器?記錄重入次數。

      • ReentrantLock?通過?getHoldCount()?獲取重入次數。

    Java 中的可重入鎖

    • synchronized?關鍵字:隱式支持可重入。
    • ReentrantLock:顯式支持可重入,可通過?lock()?和?unlock()?方法控制。
    public class SynchronizedDomo4 {public static int count = 0;public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread t = new Thread(()->{// 真正加鎖,同時記錄鎖的持有線程synchronized(locker){ // 第一次獲取locker鎖,鎖計數器為1count++;synchronized (locker){ // 第二次獲取同一個locker鎖,鎖計數器為2count++;synchronized (locker){ // 第三次獲取同一個locker鎖,鎖計數器為3count++;}//解鎖,鎖計數器為2}//解鎖,鎖計數器為1}//真正解鎖,鎖計數器為0});t.start();t.join();System.out.println(count);//3}
    }
    public class SynchronizedDomo5 {public void A(){synchronized (this){// 子線程獲取 this 鎖,鎖計數器+1 → 1B();}}public void B(){C();}public void C(){D();}public void D(){synchronized (this){// 子線程再次獲取 this 鎖,計數+1 → 2(已持有鎖,可重入)System.out.println("hello");}}public static void main(String[] args) throws InterruptedException {SynchronizedDomo5 s = new SynchronizedDomo5();Thread t = new Thread(()->{s.A();});t.start();}
    }

    雖然?A()?和?D()?都使用?synchronized (this)?加鎖,但由于鎖是可重入的,同一個線程可以在持有鎖的狀態下嵌套調用其他同步方法,不會導致死鎖。

    概念互斥(Mutual Exclusion)鎖競爭(Lock Contention)可重入性(Reentrancy)
    目標保護共享資源減少鎖沖突避免自我阻塞
    實現手段鎖機制鎖優化或無鎖算法鎖計數器
    關聯性互斥導致鎖競爭高競爭降低性能可重入減少死鎖
    關鍵點同一時間只有一個線程能訪問共享資源。多個線程爭奪同一把鎖,導致阻塞和上下文切換。同一個線程可多次獲取同一把鎖。

    6. 死鎖

    兩個或多個線程互相持有對方需要的資源,導致所有線程都無法繼續執行的狀態。

    死鎖的四個必要條件(Coffman條件)

    1. 互斥條件:資源不能被共享,同一時間只能被一個線程占用。
    2. 占有并等待:線程至少已經持有一個資源,同時請求其他線程持有的資源。
    3. 不可搶占:線程已獲得的資源不能被其他線程強行搶占,只能自己釋放。
    4. 循環等待:存在一個線程的循環等待鏈,每個線程都在等待下一個線程所占用的資源。

    public class SynchronizedDomo6 {public static void main(String[] args) {Object locker1 = new Object();Object locker2 = new Object();Thread t1 = new Thread(()->{synchronized (locker1){//休眠1s,為線程2爭取獲得locker2的時間try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//嘗試獲取locker2synchronized (locker2){System.out.println("t1");}}});Thread t2 = new Thread(()->{synchronized (locker2){//休眠1s,為線程1爭取獲得locker1的時間try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//嘗試獲取locker1synchronized (locker1){System.out.println("t2");}}});t1.start();t2.start();}
    }

    ?

    • t1?先獲取?locker1?鎖,然后休眠 1 秒,在這 1 秒內,t2?有機會獲取?locker2?鎖。
    • t2?先獲取?locker2?鎖,然后休眠 1 秒,在這 1 秒內,t1?持有?locker1?鎖。
    • 當?t1?休眠結束后嘗試獲取?locker2?鎖時,t2?已持有?locker2?鎖;而當?t2?休眠結束后嘗試獲取?locker1?鎖時,t1?已持有?locker1?鎖。
    • 這樣就形成了?t1?等待?t2?釋放?locker2?鎖,t2?等待?t1?釋放?locker1?鎖的情況,兩個線程相互等待對方釋放鎖,從而導致死鎖。程序卡死。
    如何避免死鎖
    1. 破壞互斥條件:不是所有資源都能這樣做(如打印機必須互斥使用)

    2. 破壞占有并等待

      • 線程在開始時就獲取所有需要的鎖,否則不獲取任何鎖。

      • 使用tryLock()等非阻塞獲取鎖的方法

    3. 破壞不可搶占條件

      • 使用可響應中斷的鎖(如ReentrantLock

      • 設置獲取鎖的超時時間

    4. 破壞循環等待條件

      • 對資源進行排序,按固定順序獲取鎖

      • 使用資源分配圖算法檢測

    7. volatile?vs?synchronized

    特性volatilesynchronized
    可見性? 保證可見性? 保證可見性
    原子性? 不保證原子性(如?i++? 保證原子性
    指令重排序? 禁止重排序? 禁止重排序
    性能開銷較小,適合輕量級同步開銷較大,適合重量級同步
    使用場景狀態標志、單次初始化、禁止重排序復合操作、方法或代碼塊同步

    本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
    如若轉載,請注明出處:http://www.pswp.cn/web/80629.shtml
    繁體地址,請注明出處:http://hk.pswp.cn/web/80629.shtml
    英文地址,請注明出處:http://en.pswp.cn/web/80629.shtml

    如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

    相關文章

    Vue 3.0中響應式依賴和更新

    響應式依賴和更新是Vue 3.0中最重要的機制&#xff0c;其核心代碼如下&#xff0c;本文將結合代碼對這個設計機制作出一些解釋。 // 全局依賴存儲&#xff1a;WeakMap<target, Map<key, Set<effect>>> const targetMap new WeakMap();// 當前活動的副作用函…

    一、內存調優

    一、內存調優 什么是內存泄漏 監控Java內存的常用工具 內存泄露的常見場景 內存泄露的解決方案 內存泄露與內存溢出的區別 內存泄露&#xff1a;在Java中如果不再使用一個對象&#xff0c;但是該對象依然在GC ROOT的引用鏈上&#xff0c;這個對象就不會被垃圾回收器回收&…

    Linux /etc/rc.d/init.d/

    在傳統的 SysV init 系統中&#xff0c;服務啟動腳本通常位于 /etc/rc.d/init.d/ 目錄下。這些腳本可以直接執行以啟動、停止或重啟服務&#xff0c;并且可以接受參數如 start, stop, status 等。 如果你想知道位于 /etc/rc.d/init.d/ 目錄下的某個腳本文件實際上指向哪里,如果…

    S7 200 smart連接Profinet轉ModbusTCP網關與西門子1200PLC配置案例

    控制要求&#xff1a;使用MODBUSTCP通信進行兩臺PLC之間的數據交換&#xff0c;由于改造現場不能改動程序&#xff0c;只留出了對應的IQ地址。于是客戶決定使用網關進行通訊把數據傳到plc。 1、讀取服務器端40001~40005地址中的數據&#xff0c;放入到VW200~VW208中&#xff1…

    打破傳統倉庫管理困局:WMS如何重構出入庫全流程

    引言 在制造業與零售業高速發展的今天&#xff0c;倉庫管理仍普遍面臨效率低、錯發漏發頻發、庫存數據滯后等痛點。人工登記導致30%的錯單率&#xff0c;貨位混亂讓揀貨耗時增加50%&#xff0c;而賬實不符引發的二次采購成本更吞噬著企業利潤。如何突破傳統管理桎梏&#xff1…

    Text2SQL在Spark NLP中的實現與應用:將自然語言問題轉換為SQL查詢的技術解析

    概述 SQL 仍然是當前行業中最受歡迎的技能之一 免責聲明&#xff1a;Spark NLP 中的 Text2SQL 注釋器在 v3.x&#xff08;2021 年 3 月&#xff09;中已被棄用&#xff0c;不再使用。如果您想測試該模塊&#xff0c;請使用 Spark NLP for Healthcare 的早期版本。 自新千年伊…

    微服務項目->在線oj系統(Java版 - 5)

    相信自己,終會成功 微服務代碼: lyyy-oj: 微服務 目錄 C端代碼 用戶題目接口 修改后用戶提交代碼(應用版) 用戶提交題目判題結果 代碼沙箱 1. 代碼沙箱的核心功能 2. 常見的代碼沙箱實現方式 3. 代碼沙箱的關鍵問題與解決方案 4. 你的代碼如何與沙箱交互&#xff1f; …

    Vue3 Element Plus 中el-table-column索引使用問題

    在 Element Plus 的 el-table 組件中&#xff0c;使用 scope.index 是不準確的。正確的索引屬性應該是 scope.$index。你的代碼需要調整為&#xff1a; vue 復制 下載 <el-button type"primary" size"default" text click"onModifyClick(scope…

    Ubuntu20.04下使用dpkg方式安裝WPS后,將WPS改為中文界面方法

    Ubuntu20.04下使用dpkg方式安裝WPS后&#xff0c;將WPS改為中文界面方法 說明方法 說明 Ubuntu20.04下使用dpkg方式安裝WPS后&#xff0c;打開WPS后&#xff0c;發現界面是英文的&#xff0c;如有需要可以按照下面的方法將其改為中文界面。 方法 cd /opt/kingsoft/wps-offic…

    【??HTTPS基礎概念與原理?】??HTTPS vs HTTP:為什么現代網站必須用HTTPS?

    以下是關于 HTTPS vs HTTP 的詳細對比分析&#xff0c;涵蓋安全性、性能差異及SEO影響&#xff0c;幫助您全面理解為何現代網站必須采用HTTPS&#xff1a; 一、安全性對比&#xff1a;HTTPS 如何解決 HTTP 的致命缺陷 1. HTTP 的安全隱患 ? 明文傳輸&#xff1a;HTTP 數據以明…

    算法刷題(Java與Python)1.二分查找

    目錄 二分查找 思路 總體 細節 問題一&#xff0c;為什么循環的條件是left<right ,為什么要有等號呢 問題二&#xff0c;為什么中間值是left (right - left) / 2 問題三&#xff0c;為什么最后返回的是左邊的值呢 情況 1&#xff1a;target 存在于數組中 情況 2&a…

    芯片生態鏈深度解析(二):基礎設備篇——人類精密制造的“巔峰對決”

    【開篇&#xff1a;設備——芯片工業的“劍與盾”】 當ASML的EUV光刻機以每秒5萬次激光脈沖在硅片上雕刻出0.13nm精度的電路&#xff08;相當于在月球表面精準定位一枚二維碼&#xff09;&#xff0c;當國產28nm光刻機在華虹產線實現“從0到1”的突破&#xff0c;這場精密制造…

    MongoTemplate 基礎使用幫助手冊

    前言 MongoDB 是一種流行的 NoSQL 數據庫&#xff0c;適合存儲大量的非結構化數據。MongoTemplate 是 Spring Data MongoDB 中的一個核心組件&#xff0c;它提供了一組豐富的 API 來與 MongoDB 進行交互。它封裝了許多常見的數據庫操作&#xff0c;使開發者能夠輕松執行 CRUD 操…

    psotgresql18 源碼編譯安裝

    環境&#xff1a; 系統&#xff1a;centos7.9 數據庫&#xff1a;postgresql18beta1 #PostgreSQL 18 已轉向 DocBook XML 構建體系&#xff08;SGML 未來將被棄用&#xff09;。需要安裝 XML 工具鏈&#xff0c;如下&#xff1a; yum install -y docbook5-style-xsl libxsl…

    C++編程起步項目

    員工信息管理系統 需求 Employee.h #pragma once#include<iostream> #include<string>using namespace std;class Employee { public:int id; // 編號string name; // 姓名string position; // 崗位int deptId; // 部門編號Employee();Employee(int id, string n…

    Linux的MySQL頭文件和找不到頭文件問題解決

    頭文件 #include <iostream> #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/statement.h> #include <cppconn/resultset.h> #include <cppconn/prepared_statement.h> #include <cppconn/exception.h&g…

    [ linux-系統 ] 命令行參數 | 環境變量

    命令行參數 命令行參數是指用戶在啟動程序時通過命令行傳遞給程序的參數。這些參數可以用于控制程序的行為、傳遞輸入數據或配置選項。 在 C/C 中&#xff0c;命令行參數通過 main 函數的參數傳遞 命令行參數列表 argc:參數的個數 argv[]&#xff1a;參數的清單 為什么要…

    新書速覽|鴻蒙HarmonyOS NEXT開發之路 卷2:從入門到應用篇

    《鴻蒙HarmonyOS NEXT開發之路 卷2&#xff1a;從入門到應用篇》 01 本書內容 《鴻蒙HarmonyOS NEXT開發之路 卷2&#xff1a;從入門到應用篇》是一本深度聚焦HarmonyOS NEXT應用開發的全方位指導書&#xff0c;內容遵循由淺入深的原則展開。全書分為基礎知識、應用開發進階和…

    經典密碼學和現代密碼學的結構及其主要區別(1)凱撒密碼——附py代碼

    密碼學是一門通過使用代碼和密碼來保護信息的藝術與科學&#xff0c;其歷史可以追溯到數千年前。古典密碼學代表了這一古老學科早期的篇章。早在計算機和現代加密算法出現之前&#xff0c;歷史上的各個文明就依靠巧妙的方法來保護機密、安全通信以及獲取戰略優勢。 古典密碼學…

    Python60日基礎學習打卡D30

    回顧&#xff1a; 導入官方庫的三種手段導入自定義庫/模塊的方式導入庫/模塊的核心邏輯&#xff1a;找到根目錄&#xff08;python解釋器的目錄和終端的目錄不一致&#xff09; # 直接導入 from random import randint print(randint(1, 10)) # 導入自定義庫 import module m…