【JavaEE】多線程進階(2)

【JavaEE】多線程進階(2)

  • 一、JUC(java.util.concurrent) 的常?類
      • 1.1 Callable 接?
      • 1.2 ReentrantLock
      • 1.3 原子類
        • 原子類的特性:
        • 常見原子類:
        • 原子類的實例:
      • 1.4 線程池
      • 1.5 信號量 Semaphore
        • 代碼實例
      • 1.6 CountDownLatch
        • 代碼實例
      • 1.7 線程安全的集合類
        • 多線程環境使? ArrayList
        • 多線程環境使?哈希表
        • Hashtable
        • ConcurrentHashMap
      • 1.8 死鎖

一、JUC(java.util.concurrent) 的常?類

博客結尾附有此篇博客的全部代碼!!!

1.1 Callable 接?

Callable 接口是 Java 中用于定義可以返回結果的任務的接口,它位于 java.util.concurrent 包中。

public interface Callable<V> {V call() throws Exception;
}

實例應用:計算1+2+…+100的值,使用Callable接口

   public static void main(String[] args) throws InterruptedException, ExecutionException {Callable<Integer> callable = new Callable<Integer>() {public Integer call() throws Exception {int sum = 0;for (int i = 0; i <= 100; i++) {sum += i;}return sum;}};Thread thread = new Thread(callable);thread.start();}

在這里插入圖片描述
原因:Thread本身不提供接受結果的方法,需要FutureTask對象來拿到結果(Thread不提供接受結果是為了更好的解耦合,將任務和線程分離開)

  • FutureTask:FutureTask 實現了 Runnable 接口,因此可以被 Thread 接受。
  • Thread類的構造函數可以接受一個 Runnable 對象,但不能接受其他類型的對象,因為 Thread 的內部邏輯是基于 Runnable 的 run() 方法實現的。

修改:

public class CallableDemo {public static void main(String[] args) throws InterruptedException, ExecutionException {Callable<Integer> callable=new Callable<Integer>() {public Integer call() throws Exception {int sum=0;for (int i = 0; i <= 100; i++) {sum+=i;}return sum;}};FutureTask<Integer> futureTask=new FutureTask<>(callable);Thread thread=new Thread(futureTask);thread.start();System.out.println(futureTask.get());}
}

通過Runnable接口計算1+2+…+100的值:

public class RunnableDemo {private static int total=0;public static void main(String[] args) throws InterruptedException {Runnable r = new Runnable(){int sum=0;public void run() {for (int i = 0; i <=100 ; i++) {sum+=i;}total=sum;}};Thread t1 = new Thread(r);t1.start();t1.join();System.out.println(total);}
}

1.2 ReentrantLock

可重?互斥鎖. 和 synchronized 定位類似, 都是?來實現互斥效果, 保證線程安全

ReentrantLock 的核心功能是通過 Lock 接口實現的,它提供了以下方法:

  • lock():獲取鎖,如果鎖已經被其他線程占用,則當前線程會阻塞,直到獲取鎖。
  • unlock():釋放鎖。
  • tryLock():嘗試獲取鎖,如果鎖可用則立即獲取,否則返回 false,不會阻塞。
  • tryLock(long timeout, TimeUnit unit):嘗試獲取鎖,如果在指定時間內無法獲取鎖,則返回 false。
  • isHeldByCurrentThread():判斷當前線程是否持有該鎖。
  • isLocked():判斷鎖是否被任何線程持有。
public class ReentrantLockDemo1 {private static int total = 0;public static void main(String[] args) throws InterruptedException {ReentrantLock locker = new ReentrantLock();Thread t1 = new Thread(() -> {for (int i = 0; i < 50000; i++) {locker.lock();total++;locker.unlock();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 50000; i++) {locker.lock();total++;locker.unlock();}});t1.start();t2.start();t1.join();t2.join();System.out.println(total);}
}

運行結果:total=100000
這里需要注意的:
因為這里解鎖需要自己手動解鎖,但是不可避免的拋出異常而導致代碼運行終止,有可能就執行不到 locker.lock();
改進:將unlocker.lock();放入finally代碼塊中
在這里插入圖片描述
ReentrantLock和synchronized對比:

  1. synchronized是關鍵字,ReentrantLock是Java的標準庫中的類
  2. synchronized是通過代碼塊執行加鎖解鎖,而ReentrantLock是通過lock()和unlock()加鎖解鎖,需要注意的是unlock()不調用問題
  3. ReentrantLock提供的tryLock(),如果成功加鎖,返回true;反之,加鎖失敗,返回false,不會出現阻塞;而且還可以設置等待時長,在這段時間后再嘗試加鎖,返回true/false。
  4. synchronized是非公平鎖,ReentrantLock默認是非公平鎖,但是可以設置為公平鎖
ReentrantLock lock = new ReentrantLock(true);
  1. 更強?的喚醒機制. synchronized 是通過 Object 的 wait / notify 實現等待-喚醒. 每次喚醒的是?個隨機等待的線程. ReentrantLock 搭配Condition 類實現等待-喚醒, 可以更精確控制喚醒某個指定線程。

1.3 原子類

原子類通過提供一系列線程安全的變量操作方法,確保在多線程環境下對變量的讀寫操作是不可分割的(即原子的)。它們利用了底層硬件的原子操作指令(如 CAS),從而避免了鎖的開銷,提高了性能。

原子類的特性:
  • 無鎖并發:原子類通過 CAS 機制實現線程安全,無需使用重量級的鎖(如 synchronized 或 ReentrantLock)。
  • 高性能:由于避免了鎖的開銷,原子類在高并發場景下通常比傳統同步機制性能更高。
  • 線程安全:原子類保證了對變量的操作是原子的,即使在多線程環境下也不會出現競態條件。
常見原子類:

(1)基本類型原子類:
AtomicInteger:用于原子操作的整數。
AtomicLong:用于原子操作的長整型。
AtomicBoolean:用于原子操作的布爾值。
(2)引用類型原子類:
AtomicReference:用于原子操作的對象引用。
AtomicStampedReference:用于原子操作的對象引用,同時帶有版本號(用于解決 ABA 問題)。
AtomicMarkableReference:用于原子操作的對象引用,同時帶有布爾標記。
(3)數組類型原子類:
AtomicIntegerArray:用于原子操作整型數組。
AtomicLongArray:用于原子操作長整型數組。
AtomicReferenceArray:用于原子操作對象引用數組。

原子類的實例:

基本類型原子類:AtomicInteger:用于原子操作的整數

public class AtomicIntegerArrayDemo1 {public static void main(String[] args) {AtomicInteger atomicInt = new AtomicInteger(2);atomicInt.incrementAndGet(); // 增加 1atomicInt.addAndGet(2);      // 增加 5atomicInt.compareAndSet(5, 10); // 如果當前值為 5,則設置為 10System.out.println(atomicInt.get());//這里獲取的是10}
}
public class AtomicIntegerArrayDemo {public static void main(String[] args) throws InterruptedException {AtomicInteger atomicInt = new AtomicInteger(0);Thread t1 = new Thread(() -> {for(int i = 0; i < 5000;i++ ){atomicInt.incrementAndGet();}});Thread t2 = new Thread(() -> {for(int i = 0; i < 5000;i++ ){atomicInt.incrementAndGet();}});t1.start();t2.start();t1.join();t2.join();System.out.println(atomicInt.get());//獲取的是10000}
}

引用類型原子類:AtomicStampedReference:用于原子操作的對象引用,同時帶有版本號。

public class AtomicStampedReferenceDemo1 {public static void main(String[] args) {AtomicStampedReference<String> ref = new AtomicStampedReference<>("Hello", 0);ref.compareAndSet("Hello", "World",0, 1); // 更新引用和版本號System.out.println(ref.getReference());//expectedStamp和initialStamp相等,// 則更新initialRef引用值為newReference,并且更新版本號}
}

compareAndSet 方法的作用:

  • 檢查當前引用值是否為 “Hello”。
  • 檢查當前版本號是否為 0。
  • 如果兩個條件都滿足,則將引用值更新為 “World”,版本號更新為 1
    在這里插入圖片描述
    數組類型原子類:AtomicReferenceArray:用于原子操作對象引用數組。
public class AtomicReferenceArrayDemo {public static void main(String[] args) {AtomicReferenceArray<String> array = new AtomicReferenceArray<>(new String[]{"Hello", "World"});array.set(1, "Java");//將索引為1的引用改為JavaSystem.out.println(array.get(1));}
}

1.4 線程池

線程池

1.5 信號量 Semaphore

Semaphore 的核心思想是通過一組許可證(permits)來控制對資源的訪問。每個線程在訪問資源之前,必須先獲取一個許可證;訪問完成后,釋放許可證。許可證的數量是有限的,當許可證用完時,后續的線程將被阻塞,直到有許可證被釋放。

代碼實例
public class SemaphoreDemo {public static void main(String[] args) throws InterruptedException {Semaphore semaphore = new Semaphore(5);System.out.println("使用第一個許可證");semaphore.acquire();System.out.println("使用第二個許可證");semaphore.acquire();System.out.println("使用第三個許可證");semaphore.acquire();System.out.println("使用第四個許可證");semaphore.acquire();
//        semaphore.release();semaphore.acquire();System.out.println("使用第五個許可證");}
}

在這里插入圖片描述
將許可證改為4張,任務還是5個:
在這里插入圖片描述
這里可以通過jconsole.exe來調試看下運行結果:
在這里插入圖片描述

還是四張許可證,但是這里釋放了一張許可證:
在這里插入圖片描述

1.6 CountDownLatch

使用多線程,經常將一個大的任務分成多個子任務,使用多線程執行子任務,提高執行效率。

怎么判斷子任務全部執行完畢呢?
這里就可以用CountDownLatch來記錄各個任務完成。

  1. 構造 CountDownLatch 實例, 初始化 10 表?有 10 個任務需要完成.
  2. 每個任務執?完畢, 都調? latch.countDown() . 在 CountDownLatch 內部的計數器同時?減.
  3. 主線程中使? latch.await(); 阻塞等待所有任務執?完畢. 相當于計數器為 0 了
代碼實例
public class CountDownLatchDemo {public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(3);Thread t1 = new Thread(()->{for(int i=0;i<3;i++){try {Thread.sleep((long) (Math.random() * 2000));latch.countDown();} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();latch.await(); // 阻塞主線程,直到計數器為 0System.out.println("所有任務執行完畢");}
}

1.7 線程安全的集合類

Vector, Stack, HashTable, 是線程安全的(不建議?), 其他的集合類不是線程安全的

多線程環境使? ArrayList

讓ArrayList變成線程安全:

  1. ??使?同步機制 (synchronized 或者 ReentrantLock)
  2. Collections.synchronizedList(new ArrayList);
    返回List的各種關鍵方法都帶synchronized,這種做法類似于Vector, Stack
  3. 使? CopyOnWriteArrayList
    讀操作:讀操作直接訪問底層數組,不需要加鎖,因此性能很高。
    寫操作:
  • 創建底層數組的完整副本。
  • 在副本上進行修改操作。
  • 將副本替換為原始數組。
    這種操作的效率相對低效,因為每次都需要復制整個數組。
多線程環境使?哈希表

HashMap 本?不是線程安全的.
在多線程環境下使?哈希表可以使?:
? Hashtable
? ConcurrentHashMap

Hashtable
  • 使用全局鎖(synchronized)保護整個哈希表(這意味著在任何時刻,只有一個線程可以修改哈希表,其他線程必須等待),所有操作(包括讀寫)都會鎖住整個表。
  • 這種機制簡單但效率低下,尤其是在高并發場景下,容易導致線程阻塞。

存在缺點:

  1. 如果多線程訪問同?個 Hashtable 就會直接造成鎖沖突.
  2. size 屬性也是通過 synchronized 來控制同步, 也是?較慢的.
  3. ?旦觸發擴容, 就由該線程完成整個擴容過程. 這個過程會涉及到?量的元素拷?, 效率會?常低.
ConcurrentHashMap
  • 使用分段鎖(Segment)機制,將哈希表分為多個段,每個段有自己的鎖。
  • JDK 1.8 以后,進一步優化為基于 CAS 和 synchronized 的鎖機制,結合數組 + 鏈表 + 紅黑樹的數據結構。
  • 讀操作通常不需要加鎖,寫操作的鎖粒度更細,大大減少了鎖競爭。

優化擴容:

  1. 發現需要擴容的線程, 只需要創建?個新的數組, 同時只搬?個元素過去.
  2. 擴容期間, 新?數組同時存在.
  3. 后續每個來操作 ConcurrentHashMap 的線程, 都會參與搬家的過程. 每個操作負責搬運??部分元素.
  4. 搬完最后?個元素再把?數組刪掉.
  5. 這個期間, 插?只往新數組加.
  6. 這個期間, 查找需要同時查新數組和?數組

1.8 死鎖

線程安全

此篇博客的全部代碼!!!

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

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

相關文章

[漏洞篇]XSS漏洞詳解

[漏洞篇]XSS漏洞 一、 介紹 概念 XSS&#xff1a;通過JS達到攻擊效果 XSS全稱跨站腳本(Cross Site Scripting)&#xff0c;為避免與層疊樣式表(Cascading Style Sheets, CSS)的縮寫混淆&#xff0c;故縮寫為XSS。這是一種將任意 Javascript 代碼插入到其他Web用戶頁面里執行以…

越早越好!8 個反直覺的金錢真相|金錢心理學

很多人都追求財富自由&#xff0c;但成功的人少之又少。 這可能是因為&#xff0c;人們往往忽略了一些金錢的真相和常識。 01 金錢常識 & 真相 為了構建健康的金錢觀&#xff0c;我讀了一本有點反直覺&#xff0c;有點像雞湯&#xff0c;但都是財富真相的書。 來自 Morg…

Spring Boot/Spring Cloud 整合 ELK(Elasticsearch、Logstash、Kibana)詳細避坑指南

我們在開發中經常會寫日志&#xff0c;所以需要有個日志可視化界面管理&#xff0c;使用ELK可以實現高效集中化的日志管理與分析&#xff0c;提升性能穩定性&#xff0c;滿足安全合規要求&#xff0c;支持開發運維工作。 下述是我在搭建ELK時遇到的許許多多的坑&#xff0c;希望…

AI編程: 一個案例對比CPU和GPU在深度學習方面的性能差異

背景 字節跳動正式發布中國首個AI原生集成開發環境工具&#xff08;AI IDE&#xff09;——AI編程工具Trae國內版。 該工具模型搭載doubao-1.5-pro&#xff0c;支持切換滿血版DeepSeek R1&V3&#xff0c; 可以幫助各階段開發者與AI流暢協作&#xff0c;更快、更高質量地完…

手機屏幕摔不顯示了,如何用其他屏幕臨時顯示,用來導出資料或者清理手機

首先準備一個拓展塢 然后 插入一個外接的U盤 插入鼠標 插入有數字小鍵盤區的鍵盤 然后準備一根高清線&#xff0c;一端鏈接電腦顯示器,一端插入拓展塢 把拓展塢的連接線&#xff0c;插入手機充電口&#xff08;可能會需要轉接頭&#xff09; 然后確保手機開機 按下鍵盤…

探索鏈表的奧秘:C語言中的查找操作與鏈表打印

目錄 鏈表的基本結構 頭插法 打印鏈表 按位置查找 按值查找 主函數 查找操作 示例運行 輸出示例 總結 在數據結構的學習中&#xff0c;鏈表是一種非常重要的線性結構。它的動態特性使得在插入和刪除操作時比數組更為高效。今天&#xff0c;我們將繼續探討鏈表的操作&…

第八屆藍橋杯單片機省賽

什么&#xff1f;你把最近幾屆省賽真題做完已經無題可做了&#xff0c;那不妨來看看老古董第八屆省賽的題目吧&#xff01; 附件&#xff1a;第八屆藍橋杯單片機省賽 一、數碼管 1.頁面流轉 以上的頁面流轉功能可以用下圖總結&#xff1a; #mermaid-svg-38fdQpdydbMy5CyP {fo…

win10電腦鼠標速度突然變的很慢?

電腦鼠標突然變很慢&#xff0c;殺毒檢測后沒問題&#xff0c;鼠標設置也沒變&#xff0c;最后發現可能是誤觸鼠標的“DPI”調節鍵。 DPI調節鍵在鼠標滾輪下方&#xff0c;再次點擊即可恢復正常鼠標速度。 如果有和-的按鍵&#xff0c;速度變快&#xff0c;-速度變慢。 圖源&…

1-002:MySQL InnoDB引擎中的聚簇索引和非聚簇索引有什么區別?

在 MySQL InnoDB 存儲引擎 中&#xff0c;索引主要分為 聚簇索引&#xff08;Clustered Index&#xff09; 和 非聚簇索引&#xff08;Secondary Index&#xff09;。它們的主要區別如下&#xff1a; 1. 聚簇索引&#xff08;Clustered Index&#xff09; 定義 聚簇索引是表數…

【解決哈希沖突】

哈希沖突 如果兩個不同的 key 通過哈希函數得到了相同的索引&#xff0c;這種情況就叫做「哈希沖突」。 哈希沖突不可能避免&#xff0c;只能在算法層面妥善處理出現哈希沖突的情況。 哈希沖突是一定會出現的&#xff0c;因為這個 hash 函數相當于是把一個無窮大的空間映射到…

文件操作詳解(萬字長文)

C語言文件操作 一、為什么使用文件&#xff1f;二、文件分類三、文件的打開和關閉四、文件的順序讀寫4.1fputc4.2fgetc4.3fputs4.4fgets4.5 fprintf4.6 fscanf4.7 fwrite4.8 fread 五、文件的隨機讀寫5.1 fseek5.2 ftell和rewind六、文件讀取結束的判定七、文件緩沖區 一、為什…

基于 JDBC 的后端與 MySQL 數據庫交互 javaweb

一、了解JDBC 二、添加MySQL的JDBC驅動包 三、使用JDBC連接數據庫應用&#x1f517; 3.1創建一個包 3.2 查找實例 3.3 修改添加刪除實例 四、封裝 &#x1f4e6; DBConnection.java MysqlUtil.java 測試使用一下 測試1 測試2 在后端開發中&#xff0c;與數據庫進行交…

貪心算法--

1.檸檬水找零 link:860. 檸檬水找零 - 力扣&#xff08;LeetCode&#xff09; code class Solution { public:bool lemonadeChange(vector<int>& bills) {// 貪心算法&#xff0c; 優先花出大面額bill&#xff0c; 盡可能保護小面額billint five 0, ten 0;// 不…

基于YOLO11深度學習的電瓶車進電梯檢測與語音提示系統【python源碼+Pyqt5界面+數據集+訓練代碼】

《------往期經典推薦------》 一、AI應用軟件開發實戰專欄【鏈接】 項目名稱項目名稱1.【人臉識別與管理系統開發】2.【車牌識別與自動收費管理系統開發】3.【手勢識別系統開發】4.【人臉面部活體檢測系統開發】5.【圖片風格快速遷移軟件開發】6.【人臉表表情識別系統】7.【…

github生成badges的方法

在Github頁面上生成類似下面這樣的badge的方法 你可以通過以下步驟在GitHub個人主頁的README中創建類似的技術棧徽章&#xff1a; 一、使用 Shields.io 生成徽章 Shields.io 是一個開源徽章生成工具&#xff0c;支持自定義文本、顏色、圖標等參數。 1. 基礎模板 https://…

vue3 二次封裝uni-ui中的組件,并且組件中有 v-model 的解決方法

在使用uniappvue3開發中&#xff0c; 使用了uni-ui的組件&#xff0c;但是我們也需要自定義組件&#xff0c;比如我要自定一個picker 的組件&#xff0c; 是在 uni-data-picker 組件的基礎上進行封裝的 父組件中的代碼 <classesselect :selectclass"selectclass"…

Spring Boot啟動流程及源碼實現深度解析

Spring Boot啟動流程及源碼實現深度解析 一、啟動流程概述 Spring Boot的啟動流程圍繞SpringApplication類展開&#xff0c;核心流程可分為以下幾個階段&#xff1a; 初始化階段&#xff1a;推斷應用類型&#xff0c;加載ApplicationContextInitializer和ApplicationListene…

爬蟲案例七Python協程爬取視頻

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、Python協程爬取視頻 前言 提示&#xff1a;這里可以添加本文要記錄的大概內容&#xff1a; 爬蟲案例七協程爬取視頻 提示&#xff1a;以下是本篇文章正文…

uni-app開發的App和H5嵌套封裝的App,以及原生App有什么區別

uni-app 開發的 App 和 H5 嵌套封裝的 App 是兩種不同的開發模式&#xff0c;雖然它們都可以實現跨平臺開發&#xff0c;但在技術實現、性能、功能支持等方面有顯著區別。以下是詳細對比&#xff1a; 1. uni-app 開發的 App uni-app 是一個基于 Vue.js 的跨平臺開發框架&#…

Python 爬蟲實戰案例 - 獲取拉勾網招聘職位信息

引言 拉勾網&#xff0c;作為互聯網招聘領域的佼佼者&#xff0c;匯聚了海量且多樣的職位招聘信息。這些信息涵蓋了從新興科技領域到傳統行業轉型所需的各類崗位&#xff0c;無論是初出茅廬的應屆生&#xff0c;還是經驗豐富的職場老手&#xff0c;都能在其中探尋到機遇。 對…