JVM——Synchronized:同步鎖的原理及應用

引入

在多線程編程的世界里,共享資源的訪問控制就像一場精心設計的交通管制,而Synchronized作為Java并發編程的基礎同步機制,扮演著"交通警察"的關鍵角色。

并發編程的核心矛盾

當多個線程同時訪問共享資源時,"線程安全"問題便應運而生。想象一個銀行賬戶的場景:若兩個線程同時執行扣款操作,可能導致賬戶余額出現負數或不一致的情況。這種情況下,我們需要一種機制來確保在同一時刻只有一個線程能操作共享資源,這就是同步鎖的核心使命。

Synchronized的歷史地位

作為Java語言內置的同步機制,Synchronized從JDK1.0時代便已存在。早期版本中,它因"重量級鎖"的標簽被認為性能不佳,但隨著JDK6之后的一系列優化(如偏向鎖、輕量級鎖的引入),其性能表現已大幅提升,在很多場景下甚至優于ReentrantLock

同步鎖的三大核心特性

  • 互斥性:確保同一時刻只有一個線程獲取鎖并執行同步代碼

  • 可見性:保證釋放鎖時對共享變量的修改能立即被其他線程看見

  • 有序性:禁止指令重排序,確保同步代碼塊內的操作按順序執行

這些特性通過JVM底層的監視器鎖(Monitor)機制實現,是理解Synchronized的關鍵切入點。

鎖的狀態與類型:從無鎖到重量級鎖的演進

JVM視角下的鎖狀態體系

從JVM實現層面看,鎖的狀態可分為4種,每種狀態對應不同的競爭程度和性能特征:

狀態值狀態名稱競爭程度典型場景Mark Word結構(64位JVM)
0無鎖狀態無競爭單線程訪問對象哈希碼(25bit) + GC分代年齡(4bit) + 鎖標志位(01) + 偏向鎖標志(0)
1偏向鎖狀態輕微競爭單線程重復訪問線程ID(54bit) + GC分代年齡(4bit) + 鎖標志位(01) + 偏向鎖標志(1)
2輕量級鎖狀態中度競爭多線程自旋等待指向棧中鎖記錄的指針(62bit) + 鎖標志位(00)
3重量級鎖狀態高度競爭多線程阻塞等待指向監視器對象的指針(62bit) + 鎖標志位(10)

鎖狀態轉換圖

無鎖狀態 ? 偏向鎖狀態 ? 輕量級鎖狀態 ? 重量級鎖狀態↑ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ↓└────────────── 鎖膨脹 ────────────┘

這種狀態轉換是單向不可逆的,只能從低競爭狀態向高競爭狀態升級(鎖膨脹),而不能降級,這是JVM為了優化性能做出的設計選擇。

偏向鎖:單線程優化的利器

偏向鎖的核心思想

偏向鎖是JVM對"同一線程多次獲取同一鎖"場景的優化,它通過在對象頭中記錄線程ID的方式,避免重復獲取鎖的開銷。當一個線程首次獲取對象鎖時,JVM會將偏向鎖標志位設為1,并將線程ID寫入Mark Word,后續該線程再次訪問時無需進行CAS操作,直接判斷Mark Word中的線程ID是否與當前線程一致。

偏向鎖的激活與撤銷

  • 激活條件:JVM參數-XX:+UseBiasedLocking(JDK6后默認啟用)

  • 撤銷場景

    • 當其他線程嘗試獲取偏向鎖時,會觸發偏向鎖撤銷

    • 調用wait()/notify()等方法時,偏向鎖會升級為輕量級鎖

    • 偏向鎖可以通過BiasedLockingStartupDelay參數控制延遲激活時間

典型應用場景

偏向鎖最適合"單線程反復訪問同步資源"的場景,例如:

public class BiasedLockDemo {private Object lock = new Object();public void doWork() {synchronized (lock) {// 單線程頻繁執行的業務邏輯}}
}

在這種場景下,偏向鎖能消除幾乎所有的鎖獲取開銷。

輕量級鎖:自旋等待的藝術

輕量級鎖的實現原理

當偏向鎖被撤銷或遇到輕度競爭時,鎖會升級為輕量級鎖。其核心原理是通過CAS操作在棧幀中創建"鎖記錄"(Lock Record),并將對象頭的Mark Word替換為指向鎖記錄的指針:

  1. 線程在棧中創建Lock Record,復制對象頭Mark Word到Lock Record(Displaced Mark Word)

  2. 嘗試用CAS將對象頭Mark Word替換為指向Lock Record的指針

  3. CAS成功則獲取鎖,失敗則進入自旋等待

  4. 自旋一定次數后仍未獲取鎖,則升級為重量級鎖

自旋優化的權衡

自旋等待(Spin Waiting)是指線程不放棄CPU,而是循環檢查鎖是否可用。這種方式避免了線程阻塞的開銷,但會消耗CPU資源。JVM通過-XX:PreBlockSpin參數控制自旋次數,默認值為10次。在多核CPU環境下,自旋優化能顯著提升輕度競爭場景的性能。

輕量級鎖與偏向鎖的對比

特性偏向鎖輕量級鎖
競爭程度無競爭輕度競爭
加鎖方式CAS記錄線程IDCAS修改對象頭指針
解鎖開銷幾乎無需CAS還原Mark Word
典型場景單線程反復訪問多線程交替訪問

重量級鎖:操作系統級別的同步

重量級鎖的底層實現

當輕量級鎖自旋超過閾值或競爭更加激烈時,鎖會膨脹為重量級鎖。此時JVM會調用操作系統的互斥量(Mutex)來實現線程阻塞,具體過程包括:

  1. 創建與對象關聯的監視器(Monitor)對象

  2. 線程進入監視器的等待隊列,狀態變為BLOCKED

  3. 釋放CPU資源,等待操作系統調度喚醒

  4. 喚醒后重新嘗試獲取鎖

重量級鎖的性能開銷

重量級鎖的性能開銷主要來自:

  • 線程狀態切換(用戶態→內核態→用戶態)

  • 操作系統調度器的上下文切換

  • 等待隊列的管理開銷

在JDK6之前,Synchronized默認使用重量級鎖,這也是其"性能不佳"印象的來源。但經過鎖升級優化后,重量級鎖的使用場景已大幅減少。

鎖膨脹的觸發條件

鎖狀態從低到高升級的關鍵觸發條件包括:

  • 偏向鎖遇到其他線程競爭

  • 輕量級鎖自旋次數超過閾值(默認10次)

  • 調用Object.wait()等會導致線程阻塞的方法

  • 鎖競爭持續時間超過自旋優化的收益臨界點

Synchronized與Java內存模型(JMM)的深層聯系

JMM的核心架構

Java內存模型定義了線程和主內存之間的抽象關系:

  • 主內存:所有線程共享的內存區域,存儲共享變量

  • 工作內存:每個線程私有的內存區域,存儲共享變量的副本

這種架構導致了一個核心問題:線程間如何保證共享變量的可見性?Synchronized通過以下機制解決這一問題:

Synchronized的內存語義

當線程執行synchronized同步塊時,會遵循以下內存規則:

  1. 進入同步塊

    • 從主內存讀取共享變量的最新值到工作內存

    • 清空工作內存中與同步塊相關的變量副本

    • 保證同步塊內操作的有序性(禁止指令重排序)

  2. 退出同步塊

    • 將工作內存中的變量修改刷新到主內存

    • 確保所有對共享變量的修改對其他線程可見

    • 建立happens-before關系,保證后續線程能看到最新數據

這種機制通過JVM在編譯時生成的monitorentermonitorexit指令實現,確保了同步操作的可見性和有序性。

happens-before原則與Synchronized

JMM中的happens-before原則定義了操作之間的偏序關系,其中與Synchronized相關的規則包括:

  • 監視器鎖規則:對一個鎖的解鎖操作happens-before于后續對該鎖的加鎖操作

  • 程序順序規則:同步塊內的操作按程序順序執行

  • 傳遞性:若A happens-before B且B happens-before C,則A happens-before C

這些規則共同保證了Synchronized同步塊內操作的正確性,例如:

private int x = 0;
private Object lock = new Object();
?
public void update() {synchronized (lock) {x = 1; // 操作1x = 2; // 操作2} // 解鎖操作,happens-before后續加鎖操作
}
?
public void read() {synchronized (lock) { // 加鎖操作,happens-after解鎖操作assert x == 2; // 一定成立}
}

由于解鎖操作happens-before加鎖操作,讀操作必然能看到寫操作的最新結果。

其他同步解決方案:與Synchronized的對比與互補

ReentrantLock:靈活的顯式鎖

ReentrantLock的核心特性

ReentrantLock(可重入鎖)是JUC包中提供的同步工具,與Synchronized相比具有以下優勢:

  • 顯式鎖控制:通過lock()unlock()方法顯式獲取和釋放鎖

  • 可中斷獲取鎖:支持lockInterruptibly()方法,可響應中斷

  • 公平鎖機制:支持公平鎖模式,保證線程獲取鎖的順序

  • 條件變量:通過newCondition()方法創建條件變量,實現更靈活的等待/通知機制

公平鎖與非公平鎖的實現差異

ReentrantLock支持兩種鎖模式:

  • 非公平鎖(默認):新線程可能在等待隊列頭部線程之前獲取鎖,性能更高

  • 公平鎖:嚴格按照線程等待順序獲取鎖,避免饑餓

// 創建公平鎖
ReentrantLock fairLock = new ReentrantLock(true);
?
// 使用示例
fairLock.lock();
try {// 同步代碼塊
} finally {fairLock.unlock();
}

公平鎖通過AQS(AbstractQueuedSynchronizer)的等待隊列實現,而非公平鎖在獲取鎖時會先嘗試直接獲取,可能跳過等待隊列中的線程。

ReentrantLock與Synchronized的對比

特性SynchronizedReentrantLock
鎖獲取方式隱式(自動加鎖/解鎖)顯式(手動調用方法)
可重入性支持支持
公平性非公平可選擇公平/非公平
鎖中斷不支持支持
條件變量不支持支持
性能(無競爭)優(偏向鎖優化)略遜
性能(高競爭)略遜優(可中斷、公平鎖)

ReadWriteLock:讀寫分離的同步策略

讀寫鎖的核心思想

ReadWriteLock(讀寫鎖)將鎖分為讀鎖和寫鎖,允許多個線程同時獲取讀鎖,但同一時刻只能有一個線程獲取寫鎖。這種設計特別適合"讀多寫少"的場景,例如緩存系統:

public class Cache {private final ReadWriteLock lock = new ReentrantReadWriteLock();private Map<String, Object> data = new HashMap<>();// 讀操作獲取讀鎖public Object get(String key) {lock.readLock().lock();try {return data.get(key);} finally {lock.readLock().unlock();}}// 寫操作獲取寫鎖public void put(String key, Object value) {lock.writeLock().lock();try {data.put(key, value);} finally {lock.writeLock().unlock();}}
}

讀寫鎖的狀態管理

ReentrantReadWriteLock通過一個整數(32位)來管理兩種鎖狀態:

  • 高16位:記錄讀鎖的獲取次數(可被多個線程共享)

  • 低16位:記錄寫鎖的獲取次數(僅能被一個線程持有)

這種設計使得讀寫鎖能在一個變量中維護兩種鎖狀態,提高了空間效率。

讀寫鎖的適用場景

讀寫鎖適合以下場景:

  • 讀取操作頻率遠高于寫入操作

  • 寫入操作耗時較短

  • 需要保證讀取操作的一致性

例如:

  • 配置文件讀取(很少修改,頻繁讀取)

  • 緩存系統(讀多寫少)

  • 數據庫查詢緩存(查詢頻繁,更新較少)

但需注意,讀寫鎖在寫操作頻繁的場景下性能可能不如普通互斥鎖,因為讀鎖的釋放可能導致寫鎖饑餓。

Synchronized在JDK源碼中的典型應用

容器類中的同步實現

StringBuffer的同步實現

StringBuffer是JDK中典型的線程安全容器,其所有關鍵方法都使用Synchronized修飾:

public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {// 構造函數public StringBuffer() {super(16);}// 同步追加方法public synchronized StringBuffer append(String str) {toStringCache = null;super.append(str);return this;}// 同步插入方法public synchronized StringBuffer insert(int offset, char str[]) {toStringCache = null;super.insert(offset, str);return this;}// 其他同步方法...
}

這種實現方式保證了StringBuffer在多線程環境下的安全性,但也意味著所有操作都需要獲取鎖,在高并發場景下可能成為性能瓶頸。

Vector的同步機制

Vector與ArrayList功能相似,但所有操作都是同步的:

public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {// 同步添加元素public synchronized boolean add(E e) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true;}// 同步獲取元素public synchronized E get(int index) {if (index >= elementCount)throw new ArrayIndexOutOfBoundsException(index);return elementData(index);}// 其他同步方法...
}

與StringBuffer類似,Vector的同步實現保證了線程安全,但在并發環境下性能不如非同步容器。JDK推薦在非必要時使用ArrayList,僅在需要線程安全時通過Collections.synchronizedList(new ArrayList<>())包裝。

基礎類庫中的同步應用

Hashtable的同步實現

Hashtable是Java早期的線程安全哈希表,其實現方式與Vector類似:

public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable {// 同步put方法public synchronized V put(K key, V value) {// 檢查key是否為nullif (key == null) {throw new NullPointerException();}V oldValue = get(key);putVal(key, value, false);return oldValue;}// 同步get方法public synchronized V get(Object key) {if (key == null) {throw new NullPointerException();}Entry<?,?> e = getEntry(key);return (e == null) ? null : (V)e.value;}// 其他同步方法...
}

由于Hashtable的同步粒度較大(整個哈希表),在高并發場景下性能較差,因此JDK后來提供了ConcurrentHashMap作為替代方案,其采用分段鎖機制大幅提升了并發性能。

自定義同步工具的基礎

Synchronized也是JDK中許多自定義同步工具的實現基礎,例如:

public class Semaphore {// 內部通過AQS實現,但AQS的底層操作依賴于CAS和Monitorpublic Semaphore(int permits) {sync = new NonfairSync(permits);}// 其他方法...
}

雖然Semaphore的上層接口不直接使用Synchronized,但底層AQS的實現仍然依賴于JVM的監視器鎖機制,體現了Synchronized在JDK中的基礎地位。

Synchronized的最佳實踐與性能優化

精細化同步范圍

最小化同步代碼塊

// 反例:同步范圍過大
public void badPractice() {synchronized (this) {// 非共享資源操作,無需同步loadConfig();// 共享資源操作,需要同步updateSharedData();// 非共享資源操作,無需同步logOperation();}
}// 正例:縮小同步范圍
public void goodPractice() {// 非共享資源操作loadConfig();// 僅同步必要的代碼塊synchronized (this) {updateSharedData();}// 非共享資源操作logOperation();
}

通過縮小同步范圍,可以減少線程競爭,提高并發性。基本原則是:只對真正訪問共享資源的代碼加鎖

同步對象的選擇

優先使用私有鎖對象

private final Object lock = new Object();public void operation() {synchronized (lock) {// 同步代碼}
}

私有鎖對象避免了外部代碼直接訪問鎖,減少了鎖競爭的意外風險。

避免使用this作為鎖對象

public void badLock() {synchronized (this) { // 危險!外部可能獲取this鎖導致死鎖// 同步代碼}
}

除非類本身設計為線程安全的同步組件,否則應避免使用this作為鎖對象。

利用鎖的可重入性

可重入性的實際應用

public class ReentrantDemo {public synchronized void method1() {System.out.println("進入method1");method2(); // 調用同步方法method2System.out.println("退出method1");}public synchronized void method2() {System.out.println("進入method2");// 其他操作System.out.println("退出method2");}
}

在上述示例中,method1調用method2時,由于Synchronized鎖的可重入性,線程無需再次獲取鎖,避免了死鎖風險。可重入性是通過鎖的計數器實現的,每次進入同步塊時計數器加1,退出時減1,當計數器為0時才真正釋放鎖。

可重入性與繼承場景

class Parent {public synchronized void operation() {System.out.println("Parent operation");}
}class Child extends Parent {@Overridepublic synchronized void operation() {System.out.println("Child before");super.operation(); // 調用父類同步方法System.out.println("Child after");}
}

在繼承場景下,子類重寫同步方法并調用父類方法時,可重入性保證了鎖的正確獲取,避免了因多次加鎖導致的死鎖。

性能優化參數調整

偏向鎖相關參數

啟用/禁用偏向鎖

-XX:+UseBiasedLocking  // 啟用偏向鎖(JDK6后默認啟用)
-XX:-UseBiasedLocking // 禁用偏向鎖

偏向鎖延遲激活

-XX:BiasedLockingStartupDelay=0 // 啟動時立即激活偏向鎖(默認延遲4秒)

輕量級鎖自旋參數

自旋次數設置

-XX:PreBlockSpin=20 // 設置自旋次數(默認10次)

自適應自旋

-XX:+UseAdaptiveSpinning // 啟用自適應自旋(JDK6后默認啟用)

自適應自旋會根據前一次自旋的成功情況動態調整自旋次數,提高優化效果。

場景化選擇策略

選擇Synchronized的場景

  • 簡單同步需求:無需復雜鎖控制的場景

  • 單線程或低競爭環境:偏向鎖和輕量級鎖能發揮最佳性能

  • 代碼簡潔性優先:隱式加鎖/解鎖減少代碼量

  • 與JMM結合的場景:需要利用Synchronized的內存語義保證可見性

選擇ReentrantLock的場景

  • 需要公平鎖機制:避免線程饑餓

  • 需要可中斷鎖:響應線程中斷

  • 需要條件變量:實現更靈活的等待/通知機制

  • 高競爭環境:ReentrantLock的性能可能更優

  • 需要手動控制鎖釋放:如配合try-finally確保解鎖

選擇ReadWriteLock的場景

  • 讀多寫少的場景:如緩存、配置文件等

  • 需要讀寫分離:提高讀操作的并發性

  • 寫入操作耗時較短:避免讀鎖饑餓

總結

從"重量級鎖"到"智能鎖"的進化

回顧Synchronized的發展歷程,我們可以看到JVM團隊在性能優化上的持續努力:

  1. JDK1.0-1.5:僅支持重量級鎖,性能較差

  2. JDK6:引入偏向鎖、輕量級鎖,大幅提升性能

  3. JDK7:優化鎖膨脹路徑,減少重量級鎖的使用

  4. JDK8+:進一步優化偏向鎖的獲取和撤銷流程

這種進化使得Synchronized在無競爭和輕度競爭場景下的性能接近無鎖操作,重新成為Java并發編程的首選同步工具之一。

同步機制的選擇原則

在實際開發中,選擇同步機制應遵循以下原則:

  1. 優先使用Synchronized:對于大多數場景,Synchronized已足夠高效,且代碼更簡潔

  2. ReentrantLock作為補充:當需要公平鎖、可中斷鎖或條件變量時使用

  3. ReadWriteLock謹慎使用:僅在讀多寫少場景下使用,避免寫鎖饑餓

  4. 性能測試驗證:不同場景下鎖的性能表現可能不同,需通過實測確定最優方案

核心知識回顧

  • 鎖狀態體系:無鎖→偏向鎖→輕量級鎖→重量級鎖的狀態轉換

  • 實現原理:基于JVM監視器鎖,通過Mark Word記錄鎖狀態

  • 內存語義:保證共享變量的可見性和操作的有序性

  • 性能優化:偏向鎖、輕量級鎖、自旋等待等優化手段

  • 應用場景:結合具體業務需求選擇合適的同步方案

掌握Synchronized的原理與應用,是成為Java并發編程高手的必經之路。通過深入理解其底層實現和優化機制,我們能夠更精準地運用這一強大工具,構建高效、安全的多線程應用。

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

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

相關文章

跟著AI學習C# Day26

&#x1f4c5; Day 26&#xff1a;C# 異步編程進階 ? 學習目標&#xff1a; 深入理解 async/await 的底層機制&#xff1b;掌握 ConfigureAwait(false) 的作用與使用場景&#xff1b;避免異步死鎖&#xff0c;理解同步上下文&#xff08;Synchronization Context&#xff09…

Scrapy | 通過爬取豆瓣Top250電影信息來學習在中間件中應用隨機請求頭和代理ip

中間件的使用 1.scrapyl中間件的分類和作用1.1 scrapy中間件的分類1.2 scrapy中間的作用:預處理request和response對象2.下載中間件的使用方法:3.定義實現隨機User-Agent的下載中間件3.1 實戰:爬取豆瓣Top250電影信息3.2 中間件使用實現隨機User-Agent4. 代理ip的使用4.1思路…

【深度學習】深度學習入門:從理論到實踐的全面指南

深度學習入門&#xff1a;從理論到實踐的全面指南 深度學習&#xff1a;開啟人工智能新時代的鑰匙一、深度學習的廣泛應用場景1.1 改變生活的深度學習應用1.2 行業變革案例深度解析案例一&#xff1a;深度學習檢測皮膚癌案例二&#xff1a;移動端OCR技術突破案例三&#xff1a;…

MySQL 數據庫操作完整指南

MySQL 數據庫操作完整指南 目錄 創建數據庫 連接數據庫 創建表 約束詳解 插入數據 查詢數據 多表聯合查詢 連接查詢 高級查詢 更新數據 刪除數據 視圖詳解 存儲過程詳解 函數詳解 觸發器 事務處理 索引優化 安全性管理 備份和恢復 性能優化 刪除表和數據庫 1. 創建數據庫 基…

Java面試復習:面向對象編程、Java 8新特性與Spring

Java面試復習&#xff1a;面向對象編程、Java 8新特性與Spring 面向對象編程 概念解析&#xff1a;面向對象編程&#xff08;OOP&#xff09;是將現實世界中的概念抽象為軟件模型的編程范式&#xff0c;包括封裝、繼承和多態。 核心原理&#xff1a; 封裝&#xff1a;通過訪…

藍牙數據通訊,實現內網電腦訪問外網電腦

最近突然想到了一個可以繞開單位安全管控軟件&#xff0c;讓單位內部辦公電腦連上外網的方法。大概是這個樣子&#xff0c;讓單位辦公電腦與自己的外網電腦進行藍牙配對&#xff0c;然后用配對成功的藍牙進行網絡數據交互。這里大家可能會想用一下藍牙的網絡共享功能&#xff0…

硬件面經-具身機器人通用技術要求

目錄 簡介 場景 技術面試 設計知識點 總結 簡介 最近機器人特別的火。所以收集了一些關于機器人的面試及要求 場景 目前具身機器人賽道可謂是十分火熱,全國大大小小崛起了幾十家具身機器人公司,國外比較出名的有波士頓動力,特斯拉等,國內目前比較火的就是宇樹,眾擎…

DeepSeek生成HTML5圖片拼接工具

讓DeepSeek生成一個HTML5圖片拼接工具&#xff0c;給的提示詞如下 提示詞(prompt) 幫我生成一個可以將兩張圖片拼接到一起的程序 支持橫向拼接和豎向拼接&#xff0c;可以用html5實現功能嗎&#xff1f; DeepSeek大概20秒左右就做好了&#xff0c;而且像這么簡單的功能的話也沒…

Java面試復習指南:Java基礎、面向對象編程與并發編程

Java面試復習指南&#xff1a;Java基礎、面向對象編程與并發編程 1. Java基礎 概念解析: Java是一種面向對象的編程語言&#xff0c;具有跨平臺的特性。 核心原理: JVM負責Java程序的跨平臺運行&#xff0c;通過字節碼來實現。 高頻面試問題: Java如何實現跨平臺&#xff…

LeeCode2566替換一個數字后的最大差值

項目場景&#xff1a; 給你一個整數 num 。你知道 Danny Mittal 會偷偷將 0 到 9 中的一個數字 替換 成另一個數字。 請你返回將 num 中 恰好一個 數字進行替換后&#xff0c;得到的最大值和最小值的差為多少。 注意&#xff1a; 當 Danny 將一個數字 d1 替換成另一個數字 …

李宏毅2025《機器學習》第三講-AI的腦科學

在之前的課程中探討了如何利用大模型構建AI Agent,本科將暫時放下應用層面的探索,拿起“手術刀”和“顯微鏡”,深入剖析LLM的“大腦”,帶您踏上一場“AI腦神經科學”的旅程。課程不討論模型的訓練過程,而是假設我們擁有一個已經訓練好的、功能完備的LLM。我們的目標是:理…

CVPR 2025 | 微米級光影CT精度!復旦騰訊優圖開源Real-IAD D3數據集

【導讀】 本文介紹了復旦聯合騰訊優圖發布高精度多模態數據集Real-IAD D&#xff0c;并基于此數據集提出了一種創新的多模態融合檢測方法&#xff0c;數據集已被CVPR 2025收錄&#xff0c;并開源。>>更多資訊可加入CV技術群獲取了解哦~ 目錄 一、Real-IAD D的創新之處…

解決mysql左連接加where就不會保留左表中的全部數據的問題

在SQL中使用MySQL的LEFT JOIN操作時,如果加入了WHERE條件,確實會影響結果集的完整性,特別是如果你在WHERE條件中使用了JOIN的另一張表中的字段作為過濾條件。這是因為當你在WHERE子句中加入了對JOIN另一張表的過濾條件時,實際上你是在執行一個INNER JOIN(內連接)而非LEFT…

算法與數據結構:動態規劃DP

文章目錄 動態規劃算法全面解析一、核心思想與基本概念二、動態規劃與其他算法的區別三、動態規劃的解題步驟四、經典案例解析1. **斐波那契數列&#xff08;Fibonacci&#xff09;**2. **0-1背包問題&#xff08;0-1 Knapsack&#xff09;**3. **最長公共子序列&#xff08;LC…

Coilcraft電感上的橫線是什么意思?電感有方向么?

通常我們會認為電容、電感、電阻這幾類無源器件沒有方向性&#xff0c;在布局和貼片時可以任意方向放置&#xff0c;也不會在PCB上增加絲印標識說明其方向。與此相互印證的是&#xff0c;電容表面無絲印&#xff0c;無法識別方向&#xff1b;電阻表面一般只有包含阻值大小的數字…

通過Docker掛載nginx并修改頁面

1&#xff1a;通過docker創建nginx&#xff1a; 首先關閉原來的Docker&#xff08;防止端口號沖突&#xff09; sudo nginx -s stop 直接啟動 Nginx 進程 sudo nginx 啟動nginx&#xff1a; docker run -di --namemynginx -p 80:80 nginx cd /etc/nginx docker run -d …

力扣1124. 表現良好的最長時間段

這一題我看到數據范圍是10^4&#xff0c;暗自竊喜能用雙重循環&#xff0c;看題目是典型的前綴和哈希。不過需要一個轉換將大于8小時的轉化為1&#xff0c;其他都為-1&#xff0c;方便計算&#xff0c;之前的題目中也有這種方法。 那這樣就簡單了 class Solution { public:int…

EDA2算法速通(編者崩潰版)

這個內容是用來回憶一下EDA2涉及的算法和解題的主要步驟&#xff1a; 有疑問或發現錯誤可以私信來討論 高級綜合概述 柏拉圖優化&#xff1a;這個是來判斷是否有哪些節點能完全被其他節點優化掉。比如&#xff08;1,2&#xff09;這個節點就可以完全優化&#xff08;3,4&…

雷池waf配置第三方登錄-釘釘配置詳細教程

雷池waf配置第三方登錄-釘釘配置詳細教程 前往釘釘開放平臺https://open.dingtalk.com/ 選擇一個登錄方式登錄釘釘開放平臺 選擇一個自己所管理的組織 登錄成功后點擊我的后臺 選擇應用開發 在釘釘應用下點擊創建應用 填寫應用名稱和應用描述后點擊保存 點擊網頁…

神經網絡中的均方誤差(Mean Squared Error)詳解

引言 在機器學習和神經網絡領域&#xff0c;損失函數&#xff08;Loss Function&#xff09;是衡量模型預測值與真實值之間差異的關鍵指標。均方誤差&#xff08;Mean Squared Error, MSE&#xff09;作為一種經典的損失函數&#xff0c;因其簡單性、可解釋性和數學上的優良性…