volatile,synchronized,原子操作實現原理,緩存一致性協議

文章目錄

  • 緩存一致性協議(MESI)
  • volatile
    • 1. volatile 的作用
    • 2.volatile的底層實現
    • 3,volatile 實現單例模式的雙重鎖(面手寫)
  • synchronized
    • 1,基本用法
    • 2,可重入性
    • 3,Java對象頭
    • 4,實現原理
      • (1)代碼塊同步的實現
      • (2)方法同步的實現
    • 5,鎖的升級與對比
  • 原子操作的實現原理
    • 1,術語
    • 2,如何實現原子操作
    • 3,Java如何實現原子操作
        • CAS實現原子操作的三大問題

  • CAS及其損耗CPU性能

緩存一致性協議(MESI)

MESI 是四種緩存行狀態的縮寫:

狀態英文全稱說明
M (Modified)已修改緩存行已被當前CPU修改,與主存不一致,其他CPU不能持有該數據的有效副本
E (Exclusive)獨占緩存行僅被當前CPU持有,與主存一致,其他CPU沒有該數據的副本
S (Shared)共享緩存行被多個CPU共享,所有副本與主存一致
I (Invalid)無效緩存行數據已失效,必須從主存或其他CPU重新加載

MESI 的工作示例:

假設兩個CPU核心(Core1和Core2)訪問同一內存地址 X

  1. 初始狀態
    • X 在主存中的值為 0
    • Core1和Core2的緩存中均無 X
  2. Core1 讀取 X
    • Core1 緩存 X,狀態變為 E (Exclusive)
    • 直接從主存加載 X=0
  3. Core2 讀取 X
    • Core1 的 X 狀態降級為 S (Shared)
    • Core2 也緩存 X,狀態為 S
  4. Core1 修改 X=1
    • Core1 發送 總線事務,使 Core2 的 X 緩存行失效(狀態變為 I
    • Core1 的 X 狀態變為 M (Modified),并更新緩存值
  5. Core2 再次讀取 X
    • 發現 X 緩存行無效(狀態為 I
    • 向總線發送請求,Core1 將 X=1 寫回主存,并降級為 S
    • Core2 重新加載 X=1,狀態變為 S

volatile

volatile 是 Java 提供的一種輕量級同步機制,用于確保多線程環境下的 可見性禁止指令重排序,但它 不保證原子性

特性說明實現原理
可見性一個線程修改 volatile 變量后,其他線程立即可見新值內存屏障 + 緩存一致性協議(MESI)
有序性禁止 JVM 對 volatile 變量的讀寫操作進行重排序插入內存屏障指令
非原子性volatile 不能保證復合操作(如 i++)的原子性需配合 synchronized/CAS

1. volatile 的作用

(1) 保證可見性

  • 問題:普通變量在多線程環境下,一個線程修改后,其他線程可能無法立即看到最新值(由于 CPU 緩存)。
  • volatile 的解決方案
    • 寫操作:立即刷新到主內存,并 使其他 CPU 緩存失效
    • 讀操作:強制從主內存重新加載最新值。

(2) 禁止指令重排序

  • 問題:JVM 和 CPU 可能對指令進行優化重排,導致多線程環境下出現意外行為。
  • volatile 的解決方案
    • 通過 內存屏障(Memory Barrier) 禁止 JVM 和 CPU 對 volatile 變量的讀寫操作進行重排序。

2.volatile的底層實現

  1. 內存屏障

    • 寫操作

      • **StoreStore 屏障:**位于volatile之前,確保 volatile 寫之前的 所有普通寫操作 都已完成(刷新到主內存)
      • **StoreLoad 屏障:**位于volatile之后,禁止當前 Store 與之后的 Load 重排序;強制刷新寫緩沖區到主內存。
      // 線程1
      x = 1;                // 普通寫
      StoreStoreBarrier();  // 確保 x=1 刷入內存
      volatileVar = 2;      // volatile 寫
      StoreLoadBarrier();   // 確保 volatile 寫對所有線程可見
      
    • 讀操作

      • **LoadLoad 屏障:**位于volatile之后,防止 volatile 讀與 后續的普通讀操作 重排序
      • **LoadStore 屏障:**位于volatile之后,防止 volatile 讀與 后續的普通寫操作 重排序
      // 線程2
      int tmp = volatileVar; // volatile 讀
      LoadLoadBarrier();    // 防止后續讀重排序
      LoadStoreBarrier();   // 防止后續寫重排序
      int b = x;            // 普通讀(此時能看到線程1的 x=1)
      
  2. 緩存一致性協議

3,volatile 實現單例模式的雙重鎖(面手寫)

雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)

**JDK 版本:**JDK1.5 起

**是否 Lazy 初始化:**是(即使用到這個變量時才會實例化)

**是否多線程安全:**是

**實現難度:**較復雜

**描述:**這種方式采用雙鎖機制,安全且在多線程情況下能保持高性能。
getInstance() 的性能對應用程序很關鍵。

實例

public class Singleton {  private volatile static Singleton singleton;  private Singleton (){}  public static Singleton getSingleton() {  if (singleton == null) {  synchronized (Singleton.class) {  if (singleton == null) {  singleton = new Singleton();  }  }  }  return singleton;  }  
}
  • 私有構造器:禁止外部實例化
  • 雙重檢查
    • 第一次檢查(無鎖)
      避免每次調用 getSingleton() 都進入同步塊,提升性能。
    • 第二次檢查(加鎖后)
      防止多個線程同時通過第一次檢查后重復創建實例。
  • 同步鎖(synchronized)
    • 保證 實例化過程的原子性,防止多線程并發創建多個實例。
  • volatile 關鍵字
    • 解決 指令重排序問題,確保其他線程不會獲取到未初始化的對象。

如果不使用 volatile 關鍵字,JVM 可能會對這三個子步驟進行指令重排。

  • 為 Singleton對象分配內存
  • 將對象賦值給引用 singleton
  • 調用構造方法初始化成員變量

這種重排序會導致 singleton 引用在對象完全初始化之前就被其他線程訪問到。具體來說,如果一個線程執行到步驟 2 并設置了 singleton 的引用,但尚未完成對象的初始化,這時另一個線程可能會看到一個“半初始化”的 Singleton對象。

  • 線程 A 執行到 if (singleton == null),判斷為 true,進入同步塊。
  • 線程 B 執行到 if (singleton == null),判斷為 true,進入同步塊。

如果線程 A 執行 singleton = new Penguin() 時發生指令重排序:

  • 線程 A 分配內存并設置引用,但尚未調用構造方法完成初始化。
  • 線程 B 此時判斷 singleton != null,直接返回這個“半初始化”的對象。

這樣就會導致線程 B 拿到一個不完整的 Penguin 對象,可能會出現空指針異常或者其他問題。

于是,我們可以為 singleton 變量添加 volatile 關鍵字,來禁止指令重排序,確保對象的初始化完成后再將其賦值給 singleton。

synchronized

1,基本用法

  • 加在靜態方法上:鎖定的是類

  • 加在非靜態方法:鎖定的是方法的調用者,當前實例。

  • 修飾代碼塊:鎖定的是傳入的對象

    并發學習之synchronized,JVM內存圖,線程基礎知識-CSDN博客

2,可重入性

從互斥鎖的設計上來說,當一個線程試圖操作一個由其他線程持有的對象鎖的臨界資源時,將會處于阻塞狀態,但當一個線程再次請求自己持有對象鎖的臨界資源時,這種情況屬于重入鎖,請求將會成功。

synchronized 就是可重入鎖,因此一個線程調用 synchronized 方法的同時,在其方法體內部調用該對象另一個 synchronized 方法是允許的。

3,Java對象頭

Java對象在內存中的布局分為三部分:對象頭(Header)實例數據(Instance Data)和 對齊填充(Padding)。

對象頭是synchronized實現的基礎,它包含兩部分信息:Mark Word(標記字段)和 Klass Pointer(類型指針,指向對象的類元數據的指針,JVM通過這個指針確定對象是哪個類的實例)。

Mark Word 的格式:

鎖狀態29 bit 或 61 bit1 bit 是否是偏向鎖?2 bit 鎖標志位
無鎖001
偏向鎖線程 ID101
輕量級鎖指向棧中鎖記錄的指針此時這一位不用于標識偏向鎖00
重量級鎖指向互斥量(重量級鎖)的指針此時這一位不用于標識偏向鎖10
GC 標記此時這一位不用于標識偏向鎖11

synchronized的同步是基于進入和退出Monitor對象實現的,每個Java對象都與一個Monitor相關聯。

那什么是Monitor對象

在不同的鎖狀態下,Mark word會存儲不同的信息,這也是為了節約內存常用的設計。當鎖狀態為重量級鎖(鎖標識位=10)時,Mark word中會記錄指向Monitor對象的指針,這個Monitor對象也稱為管程監視器鎖

在這里插入圖片描述

每個對象都存在著一個 Monitor對象與之關聯。執行 monitorenter 指令就是線程試圖去獲取 Monitor 的所有權,搶到了就是成功獲取鎖了;執行 monitorexit 指令則是釋放了Monitor的所有權。

4,實現原理

JVM規范中對于synchronized的實現分為兩種方式:代碼塊同步和方法同步,它們底層采用了不同的實現策略,但最終都可以歸結為對Monitor對象的操作。

monitorenter指令是在編譯后插入到同步代碼塊的開始位置,而monitorexit是插入到方法結束處和異常處,JVM要保證每個monitorenter必須有對應的monitorexit與之配對。任何對象都有一個monitor與之關聯,當且一個monitor被持有后,它將處于鎖定狀態。線程執行到monitorenter指令時,將會嘗試獲取對象所對應的monitor的所有權,即嘗試獲得對象的鎖。

  1. 不是每個Java對象都有一個物理Monitor對象
    • 只有進入重量級鎖狀態時才會創建真正的Monitor對象
    • 偏向鎖和輕量級鎖階段,鎖信息存儲在對象頭中
  2. Monitor資源由JVM管理
    • Monitor對象不是Java層面的對象
    • 由JVM在需要時創建(通常位于C++層實現)

monitorenter 指令用于獲取對象的監視器鎖(Monitor lock),主要功能包括:

  1. 鎖獲取:嘗試獲取與對象關聯的 Monitor
  2. 鎖升級:根據競爭情況可能觸發鎖升級(偏向鎖→輕量級鎖→重量級鎖)
  3. 重入計數:支持同一線程的鎖重入
執行 monitorenter 時:
1. 檢查對象頭中的鎖標志位- 如果是無鎖狀態(01):a. 嘗試通過CAS將對象頭Mark Word替換為當前線程指針(偏向鎖)b. 成功則獲取鎖,失敗則升級為輕量級鎖- 如果是輕量級鎖(00):a. 檢查是否當前線程已持有鎖(鎖重入)b. 如果是,recursions+1c. 如果不是,自旋嘗試獲取或升級為重量級鎖- 如果是重量級鎖(10):a. 進入操作系統的互斥量等待隊列
2. 獲取成功后,對象頭將記錄鎖狀態和持有線程信息

monitorexit 指令用于釋放對象的監視器鎖,主要功能包括:

  1. 鎖釋放:釋放對 Monitor 的持有
  2. 喚醒線程:在重量級鎖狀態下喚醒等待線程
  3. 重入處理:減少重入計數,只在完全釋放時真正放開鎖
執行 monitorexit 時:
1. 檢查當前線程是否是鎖的持有者- 如果不是,拋出 IllegalMonitorStateException
2. 減少重入計數(recursions)
3. 如果重入計數歸零:a. 恢復對象頭的無鎖狀態(輕量級鎖)b. 或喚醒 EntryList 中的線程(重量級鎖)
4. 如果是同步塊結束處的 monitorexit:a. 正常退出同步區域
5. 如果是異常路徑的 monitorexit:a. 仍然確保鎖被釋放b. 重新拋出異常

(1)代碼塊同步的實現

代碼塊同步是顯式同步,通過monitorentermonitorexit指令實現:

  • 每個monitorenter必須有對應的monitorexit
  • 編譯器會為同步塊生成異常處理表,確保異常發生時也能釋放鎖
  • 可以針對任意對象進行同步

(2)方法同步的實現

方法同步是隱式同步,通過在方法訪問標志中設置ACC_SYNCHRONIZED標志實現:

  • 調用方法時會隱式獲取Monitor,沒有顯式的monitorentermonitorexit指令

  • 方法正常完成或異常拋出時會隱式釋放Monitor

  • 同步的Monitor對象是方法所屬的實例(非靜態方法)或Class對象(靜態方法)

  • JVM在方法調用時自動處理鎖的獲取和釋放

特性monitorenter/monitorexitACC_SYNCHRONIZED
實現級別字節碼指令方法訪問標志
鎖對象顯式指定任意對象隱式使用 this 或 Class 對象
異常處理顯式生成 monitorexitJVM 自動處理
可觀察性可在字節碼中直接看到只能通過訪問標志識別
優化可能性較難優化更易被 JIT 優化

5,鎖的升級與對比

鎖可以升級但不能降級,意味著偏向鎖升級成輕量級鎖后不能降級成偏向鎖。這種鎖升級卻不能降級的策略,目的是為了提高獲得鎖和釋放鎖的效率。

在這里插入圖片描述

  1. 偏向鎖:

    • 設計目的:優化只有一個線程訪問同步塊的場景

    • 實現原理:HotSpot 的作者經過研究發現,大多數情況下,鎖不僅不存在多線程競爭,而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖。當一個線程訪問同步塊并獲取鎖時,會在對象頭和棧幀中的鎖記錄里存儲鎖偏向的線程ID,以后該線程在進入和退出同步塊時不需要進行CAS操作來加鎖和解鎖,只需簡單地測試一下對象頭的Mark Word里是否存儲著指向當前線程的偏向鎖。如果測試成功,表示線程已經獲得了鎖。如果測試失敗,則需要再測試一下Mark Word中偏向鎖的標識是否設置成1(表示當前是偏向鎖):如果沒有設置,則使用CAS競爭鎖;如果設置了,則嘗試使用CAS將對象頭的偏向鎖指向當前線程

    • 升級觸發條件

      • 另一個線程嘗試獲取該鎖(產生競爭)
      • 調用 hashCode() 方法(因為偏向鎖會占用哈希碼位置)
  2. 輕量級鎖:

    • 設計目的:優化線程交替執行同步塊的場景最適合少量線程(建議≤2個活躍競爭線程)和短時間同步的場景

    • 輕量級鎖加鎖:線程在執行同步塊之前,JVM會先在當前線程的棧楨中創建用于存儲鎖記錄的空間,官方稱為Displaced Mark Word。并將對象頭中的Mark Word復制到鎖記錄中。然后線程嘗試使用CAS將對象頭中的Mark Word替換為指向鎖記錄的指針。如果成功,當前線程獲得鎖,如果失敗,表示其他線程競爭鎖,當前線程便嘗試使用自旋來獲取鎖。

    • 輕量級鎖解鎖:輕量級解鎖時,會使用原子的CAS操作將Displaced Mark Word替換回到對象頭,如果成功,則表示沒有競爭發生。如果失敗,表示當前鎖存在競爭,鎖就會膨脹成重量級鎖。

    • 升級觸發條件

      • CAS 操作失敗(表示有競爭|兩個線程的CAS操作出現重疊|競爭發生在同一時間窗口)
      • 自旋獲取鎖超過一定次數
  3. 重量級鎖

    依賴于操作系統的互斥量(mutex) 實現的,而操作系統中線程間狀態的轉換需要相對較長的時間,所以重量級鎖效率很低,但被阻塞的線程不會消耗 CPU。

原子操作的實現原理

1,術語

  • 緩存行:緩存行是CPU緩存的最小讀寫單位,通常為 64字節。三級緩存就是由緩存行組成。

    • L1/L2:每個核心獨占,減少多核競爭。
    • L3:多核共享,避免頻繁訪問內存。
  • CAS:比較并且交換。CAS需要兩個值,一個舊值,一個新值。舊值用來比較操作期間是否發生變化,如果沒有發生變化才會交換新值。

  • CPU流水線技術

    時間軸  |  指令1   |  指令2   |  指令3   |  指令4   |
    --------+----------+----------+----------+----------+
    Cycle1  |   IF1    |          |          |          |
    Cycle2  |   ID1    |   IF2    |          |          |
    Cycle3  |   EX1    |   ID2    |   IF3    |          |
    Cycle4  |   MEM1   |   EX2    |   ID3    |   IF4    |
    Cycle5  |   WB1    |   MEM2   |   EX3    |   ID4    |
    Cycle6  |          |   WB2    |   MEM3   |   EX4    |
    Cycle7  |          |          |   WB3    |   MEM4   |
    Cycle8  |          |          |          |   WB4    |
    
  • 內存順序沖突:內存順序沖突 是由于 CPU/編譯器優化導致的 指令重排問題導致的內存訪問順序與程序邏輯順序不一致,從而引發數據競爭、邏輯錯誤等問題。

2,如何實現原子操作

  1. 總線鎖
  2. 緩存鎖:處理器標記該緩存行為 “鎖定” 狀態,阻止其他核心同時訪問。

總線鎖定把CPU和內存之間的通信鎖住了,這使得鎖定期間,其他處理器不能操作其他內存地址的數據,所以總線鎖定的開銷比較大,處理器在某些場合下使用緩存鎖定代替總線鎖定來進行優化。

有兩種情況不適用緩存鎖:

  • 操作的數據沒有緩存在緩存行中,或者操作數據跨了多個緩存行會使用總線鎖
  • 某些處理器不支持

3,Java如何實現原子操作

  • **AtomicInteger**等原子類

  • 使用volatile,synchronized關鍵字

  • 使用CAS循環實現原子操作

        /** * 使用CAS實現線程安全計數器 */private void safeCount() {for (;;) {int i = atomicI.get();boolean suc = atomicI.compareAndSet(i, ++i);if (suc) {break;}}}AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 0);
    ref.compareAndSet(100, 101, stamp, stamp + 1);  // 檢查值和版本號
    
CAS實現原子操作的三大問題
  • ABA問題:CAS在操作值時,如果一個值由A變為B又變為A,那么使用CAS進行檢查時會發現它的值沒有發生變化,但是實際上卻變化了。解決思路是使用版本號,如上AtomicStampedReference
  • 循環時間長CPU開銷大,自旋CAS如果長時間不成功,會給CPU帶來非常大的執行開銷。
  • 只能保證一個共享變量的原子操作,如果是對多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖。當多個線程同時競爭同一變量時,大量 CAS 操作會失敗,導致線程自旋(循環重試)。自旋期間線程持續占用 CPU,執行無效循環,消耗 CPU 周期。

假設有1000線程并且這個CPU切換比較快速,其中一個CAS成功了,那剩余的999個就都白計算了,還不如加鎖禁止其他線程操作,這樣不會造成CPU的劇烈浪費。所以CAS只適合低烈度的并發。

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

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

相關文章

webfuture:如何屏蔽后臺發文界面的保存為新文章按鈕?

問題描述&#xff1a; 如何屏蔽后臺發文界面的保存為新文章按鈕&#xff1f; 問題解決&#xff1a;修改這個文件 /Admin/Content/Base/css/base.css 定義這個的id saveAsNewItemSubmit #saveAsNewItemSubmit{display: none;}

SpringBoot集成第三方jar的完整指南

原文地址&#xff1a;https://blog.csdn.net/weixin_43826336/article/details/141640152?ops_request_misc%257B%2522request%255Fid%2522%253A%25227d4118ef2d572ba4428caf83f1d2bb28%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id7d4118…

題目 3293: 藍橋杯2024年第十五屆決賽真題-數位翻轉

題目 3293: 藍橋杯2024年第十五屆決賽真題-數位翻轉 時間限制: 2s 內存限制: 192MB 提交: 1046 解決: 318 題目描述 小明創造了一個函數 f(x) 用來翻轉 x 的二進制的數位&#xff08;無前導 0&#xff09;。比如f(11) 13&#xff0c;因為 11 (1011)2&#xff0c;將其左右翻轉…

word為跨頁表格新加表頭和表名

問題&#xff1a; 當表格過長需要跨頁時&#xff08;如下圖所示&#xff09;&#xff0c;某些格式要求需要轉頁接排加續表。 方法一&#xff1a; 1、選中表格&#xff0c;在“表布局”區域點開“自動調整”&#xff0c;選擇“固定列寬”&#xff08;防止后續拆分表格后表格變…

Ubuntu上進行VS Code的配置

1. 安裝VS code sudo snap install code --classic 2. 安裝GCC sudo apt install build-essential 3. 安裝VS Code中文包 打開 VS Code 點擊左側活動欄中的擴展圖標(或按Ctrl+Shift+X) 在搜索框中輸入:Chinese (Simplified) 選擇由 Microsoft 提供的 中文(簡體)語言包…

vr中風--數據處理模型搭建與訓練2

位置http://localhost:8888/notebooks/Untitled1-Copy1.ipynb # -*- coding: utf-8 -*- """ MUSED-I康復評估系統&#xff08;增強版&#xff09; 包含&#xff1a;多通道sEMG數據增強、混合模型架構、標準化處理 """ import numpy as np impor…

【LLM vs Agent】從語言模型到智能體,人工智能邁出的關鍵一步

目錄 一、什么是 LLM&#xff1f;語言的天才&#xff0c;思維的起點 ? 特點小結&#xff1a; 二、什么是 Agent&#xff1f;智能的執行者&#xff0c;自主的決策者 ? 特點小結&#xff1a; 三、LLM 與 Agent 的關系&#xff1a;是工具&#xff0c;更是大腦 四、案例實戰…

安裝DockerDocker-Compose

Docker 1、換掉關鍵文件 vim /etc/yum.repos.d/CentOS-Base.repo ▽ [base] nameCentOS-$releasever - Base - Mirrors Aliyun baseurlhttp://mirrors.aliyun.com/centos/$releasever/os/$basearch/ gpgcheck1 enabled1 gpgkeyhttp://mirrors.aliyun.com/centos/RPM-GPG-KEY-C…

Perl One-liner 數據處理——基礎語法篇【匠心】

Perl&#xff08;Practical Extraction and Report Language&#xff09;是一種功能強大且靈活的腳本語言&#xff0c;因其強大的文本處理能力和簡潔的語法而廣受開發者和系統管理員的喜愛。特別是在命令行環境下&#xff0c;Perl 的 one-liner&#xff08;單行腳本&#xff09…

Go語言defer關鍵字:延遲執行的精妙設計

深度解析Go語言defer關鍵字&#xff1a;延遲執行的精妙設計 引言 在Go語言中&#xff0c;defer語句是一種獨特而強大的控制流機制&#xff0c;它通過??延遲執行??的方式解決資源管理、錯誤處理和異常恢復等關鍵問題。理解defer的工作原理是掌握Go并發編程和錯誤處理的關鍵…

C#項目07-二維數組的隨機創建

實現需求 創建二維數組&#xff0c;數組的列和寬為隨機&#xff0c;數組內的數也是隨機 知識點 1、Random類 Public Random rd new Random(); int Num_Int rd.Next(1, 100);2、數組上下限。 //定義數組 int[] G_Array new int[1,2,3,4];//一維數組 int[,] G_Array_T …

.NET WinForm圖像識別二維碼/條形碼并讀取其中內容

需求:圖像識別出一張圖片中的二維碼或者條形碼&#xff0c;并讀取其中內容。 一、安裝庫(特別注意&#xff0c;網上很多都沒說清楚) 如果是基于.net framework&#xff0c;則安裝ZXing.Net(建議0.14.0版本左右&#xff0c;具體看實際&#xff0c;版本太高&#xff0c;部分接口…

Guava限頻器RateLimiter的使用示例

文章目錄 1. 背景說明2. API與方法3. 示例代碼3.1 基礎工具方法3.2 測試任務類3.3 測試和統計方法3.4 測試兩種模式的限頻器3.5 測試緩沖時間與等待耗時 4. 完整的測試代碼5. 簡單小結 1. 背景說明 高并發應用場景有3大利器: 緩存、限流、熔斷。 也有說4利器的: 緩存、限流、…

(面試)獲取View寬高的幾種方式

Android 中獲取 View 寬高的幾種方式&#xff0c;以及它們的適用場景和注意事項&#xff1a; 1. View.getWidth() 和 View.getHeight() 原理: 直接從 View 對象中獲取已經計算好的寬度和高度。 優點: 簡單直接。 缺點: 在 onCreate()、onStart() 等生命周期方法中&#xff0…

PostgreSQL pgrowlocks 擴展

PostgreSQL pgrowlocks 擴展 pgrowlocks 是 PostgreSQL 的一個系統擴展&#xff0c;用于顯示表中行級鎖定信息。這個擴展特別適合診斷鎖爭用問題和性能調優。 一、擴展安裝與啟用 1. 安裝擴展 -- 使用超級用戶安裝 CREATE EXTENSION pgrowlocks;2. 驗證安裝 -- 查看擴展是…

JavaSE知識總結 ~個人筆記以及不斷思考~持續更新

目錄 字符串常量池 如果是創建對象還會嗎&#xff1f; Integer也是在字串常量池中復用&#xff1f; 字符串拼接 為什么String是不可變的&#xff1f; String的不可變性是怎么做的&#xff1f; 外部代碼不能創建對象&#xff1f; 構造方法不是私有的嗎&#xff1f; 怎么…

使用HTTPS進行傳輸加密

文章目錄 說明示例&#xff08;公網上的公開web&#xff09;安裝SSL證書Certbot 的 Webroot 模式 和 Standalone 模式的區別**Webroot 模式****Standalone 模式** 技術對比表Node.js 場景下的最佳實踐推薦方案&#xff1a;**Webroot 模式**Standalone 模式應急使用&#xff1a;…

驅動開發(2)|魯班貓rk3568簡單GPIO波形操控

上篇文章寫了如何下載內核源碼、編譯源碼的詳細步驟&#xff0c;以及一個簡單的官方demo編譯&#xff0c;今天分享一下如何根據板子的引腳寫自己控制GPIO進行高低電平反轉。 想要控制GPIO之前要學會看自己的引腳分布圖&#xff0c;我用的是魯班貓RK3568&#xff0c;引腳分布圖如…

ArcGIS Pro 3.4 二次開發 - 布局

環境:ArcGIS Pro SDK 3.4 + .NET 8 文章目錄 布局1 布局工程項1.1 引用布局工程項及其關聯的布局1.2 在新視圖中打開布局工程項1.3 激活已打開的布局視圖1.4 引用活動布局視圖1.5 將 pagx 導入工程1.6 移除布局工程項1.7 創建并打開一個新的基本布局1.8 使用修改后的CIM創建新…

OpenCV 圖像像素的算術操作

一、知識點 1、operator (1)、MatExpr operator (const Mat & a, const Mat & b); a、a和b的行數、列數、通道數得相同。 b、a和b的每個像素的每個通道值分別相加。 (2)、MatExpr operator (const Mat & a, const Scalar & s); a、若a…