JAVA學習-多線程

線程

? ? ? ? 線程(Thread)是一個程序內部的一條執行流程。

????????程序中如果只有一條執行流程,那這個程序就是單線程的程序。

線程的常用方法及構造器:

Thread提供的常用方法public void run()
線程的任務方法public void start()
啟動線程public String getName()
獲取當前線程的名稱,線程名稱默認是Thread-索引public void setName(String name)
為線程設置名稱public static Thread currentThread()
獲取當前執行的線程對象public static void sleep(long time)
讓當前執行的線程休眠多少毫秒后,再繼續執行public final void join()...
讓調用當前這個方法的線程先執行完!Thread提供的常見構造器public Thread(String name)
可以為當前線程指定名稱public Thread(Runnable target)
封裝Runnable對象成為線程對象public Thread(Runnable target, String name)
封裝Runnable對象成為線程對象,并指定線程名稱

多線程

????????多線程是指從軟硬件上實現的多條執行流程的技術(多條線程由CPU負責調度執行)。

創建線程的方式

????????多線程的創建方式一:繼承Thread類

? ? ? ? ? ? ? ? 操作步驟:

????????????????????????① 定義一個子類MyThread繼承線程類java.lang.Thread,重寫run()方法

????????????????????????② 創建MyThread類的對象

????????????????????????③ 調用線程對象的start()方法啟動線程(啟動后還是執行run方法的)

????????????????方式一優缺點:

????????????????????????優點:編碼簡單

????????????????????????缺點:線程類已經繼承Thread,無法繼承其他類,不利于功能的擴展。

????????注意事項:

????????直接調用run方法會當成普通方法執行,此時相當于還是單線程執行。 只有調用start方法才是啟動一個新的線程執行。

????????不要把主線程任務放在啟動子線程之前。 這樣主線程是一直先跑完的,相當于是一個單線程的效果了。

/*** 方式一:繼承Thread類*/
public class MyThread extends Thread{@Overridepublic void run() {for (int i = 1; i <= 20; i++) {System.out.println("新的線程執行:"+i);}}
}

/*** 創建線程方式一:繼承Thread類* 1.創建一個繼承Thread類的子類* 2.重寫Thread類中的run方法,將此線程要執行的代碼寫在run方法中* 3.創建Thread類的子類的對象,調用start方法**/
public class ThreadDemo1 {public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start();// 啟動線程(啟動后還是執行run方法)  交替執行
//        myThread.run();//單線程for (int i = 1; i <= 20; i++) {System.out.println("主線程執行:"+i);}}}

????????多線程的創建方式二:實現Runnable接口

? ? ? ? ? ? ? ? 操作步驟:

????????????????????????① 定義一個線程任務類MyRunnable實現Runnable接口,重寫run()方法

????????????????????????② 創建MyRunnable任務對象

????????????????????????③ 把MyRunnable任務對象交給Thread處理。

????????????????????????④ 調用線程對象的start()方法啟動線程

????????????????方式二的優缺點

????????????????????????優點:任務類只是實現接口,可以繼續繼承其他類、實現其他接口,擴展性強。

????????????????????????缺點:需要多一個Runnable對象。

/*** 方式二: 實現Runnable接口*/
public class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 1; i <= 20; i++) {System.out.println("MyRunable"+i);}}
}

public class RunnableDemo2 {public static void main(String[] args) {// 創建線程任務對象MyRunnable myRunnable = new MyRunnable();// 創建線程Thread thread = new Thread(myRunnable);// 啟動線程thread.start();/***  使用匿名內部類, 創建線程*  寫法* ① 可以創建Runnable的匿名內部類對象。* ② 再交給Thread線程對象。* ③ 再調用線程對象的start()啟動線程。*/new Thread(new Runnable(){@Overridepublic void run() {for (int i = 1; i <= 20; i++) {System.out.println("匿名內部類1:"+i);}}}).start();// 使用Lambda表達式 簡化new Thread(()-> {for (int i = 1; i <= 20; i++) {System.out.println("匿名內部類2:"+i);}}).start();for (int i = 1; i <= 20; i++) {System.out.println("main:"+i);}}}

????????線程的創建方式三:實現Callable接口,利用FutureTask類來實現

? ? ? ? ? ? ? ? 操作步驟:

????????????????????????① 創建任務對象 ? 定義一個類實現Callable接口,重寫call方法,封裝要做的事情,和要返回的數據。 ? 把Callable類型的對象封裝成FutureTask(線程任務對象)。

????????????????????????② 把線程任務對象交給Thread對象。

????????????????????????③調用Thread對象的start方法啟動線程。

????????????????????????④ 線程執行完畢后、通過FutureTask對象的的get方法去獲取線程任務執行的結果。

????????????????線程創建方式三的優缺點

????????????????????????優點:線程任務類只是實現接口,可以繼續繼承類和實現接口,擴展性強;可以在線程執行完畢后去獲取線程執行的結 果。

????????????????????????缺點:編碼復雜一點。

import java.util.concurrent.Callable;/*** 方式三:實現Callable接口*/
public class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {for (int i = 1; i <= 20; i++) {System.out.println("約嗎"+i);}return null;}
}

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;/*** 創建線程的第三種方式:實現Callable接口* 優點:* 線程任務類只是實現接口,可以繼續繼承類和實現接口,擴展性強;* 可以在線程執行完畢后去獲取線程執行的結果。**/
public class CallableDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {// 創建線程任務對象MyCallable myCallable = new MyCallable();// 創建FutureTask對象, 構造方法中傳遞線程任務對象FutureTask<String> ft = new FutureTask<>(myCallable);// 創建線程對象, 構造方法中傳遞線程對象Thread thread = new Thread(ft);// 啟動線程thread.start();/**獲取線程執行結果線程必須在啟動之后,才能獲取到線程執行結果。get()方法會阻塞當前線程,直到獲取到線程執行結果。如果get()方法在獲取到線程執行結果之前,當前線程被中斷,那么會拋出InterruptedException異常。*/String s = ft.get();System.out.println(s);for (int i = 1; i <= 20; i++) {System.out.println("吃瓜"+i);}}}

什么是線程安全問題?

????????多個線程,同時操作同一個共享資源的時候,可能會出現業務安全問題。

線程安全賣票模擬:

public class Ticket implements Runnable{private int tivket=100;@Overridepublic void run() {while (true){if (tivket <=0){break;}else{try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}tivket--;System.out.println(Thread.currentThread().getName()+ "在賣票,剩余" + tivket + "張票");}}}}

? ? ? ? 測試類:? ??

public class TicketDemo {public static void main(String[] args) {Ticket ticket = new Ticket();Thread thread1 = new Thread(ticket,"Thread1");Thread thread2 = new Thread(ticket,"Thread2");Thread thread3 = new Thread(ticket,"Thread3");thread1.start();thread2.start();thread3.start();}
}

? ?部分運行結果:

Thread1在賣票,剩余98張票
Thread2在賣票,剩余98張票
Thread3在賣票,剩余98張票
Thread1在賣票,剩余95張票
Thread3在賣票,剩余96張票
Thread2在賣票,剩余96張票
Thread3在賣票,剩余94張票
Thread1在賣票,剩余94張票
Thread2在賣票,剩余93張票
Thread3在賣票,剩余92張票
......
Thread1在賣票,剩余4張票
Thread3在賣票,剩余3張票
Thread2在賣票,剩余2張票
Thread1在賣票,剩余1張票
Thread3在賣票,剩余0張票
Thread2在賣票,剩余-1張票
Thread1在賣票,剩余-2張票

從結果可以看出,有重復賣票,還有票賣超了,這就是線程安全問題。

解決線程安全問題可以通過線程同步來解決。

線程同步

????????線程同步的核心思想:讓多個線程先后依次訪問共享資源,這樣就可以避免出現線程安全問題。

? ? ? ? 解決方案:

? ? ? ? 一:同步代碼塊

????????????????作用:把訪問共享資源的核心代碼給上鎖,以此保證線程安全。

synchronized(同步鎖) {//出現線程安全問題的代碼}// 對于當前同時執行的線程來說,同步鎖必須是同一把(同一個對象),否則會出bug。

????????鎖對象的使用規范

???????? ????????建議使用共享資源作為鎖對象,對于實例方法建議使用this作為鎖對象。

????????????????對于靜態方法建議使用字節碼(類名.class)對象作為鎖對象。

????????二:同步方法

????????????????作用:把訪問共享資源的核心方法給上鎖,以此保證線程安全。

修飾符synchronized 返回值類型 方法名稱(形參列表) 
{操作共享資源的代碼
}

原理:每次只能一個線程進入,執行完畢以后自動解鎖,其他線程才可以進來執行

????????同步方法底層原理

????????????????同步方法其實底層也是有隱式鎖對象的,只是鎖的范圍是整個方法代碼。

????????????????如果方法是實例方法:同步方法默認用this作為的鎖對象。

????????????????如果方法是靜態方法:同步方法默認用類名.class作為的鎖對象。

????????同步代碼塊好還是同步方法好?

????????????????范圍上:同步代碼塊鎖的范圍更小,同步方法鎖的范圍更大

????????????????可讀性:同步方法更好

? ? ? ? 三:Lock鎖

????????????????Lock鎖是JDK5開始提供的一個新的鎖定操作,通過它可以創建出鎖對象進行加鎖和解鎖,更靈活、更方便、更強大。

????????????????Lock是接口,不能直接實例化,可以采用它的實現類ReentrantLock來構建Lock鎖對象。

public ReentrantLock?() 獲得Lock鎖的實現類對象void lock()            獲得鎖void unlock()          釋放鎖

線程池

????????線程池就是一個可以復用線程的技術。

????????不使用線程池,用戶每發起一個請求,后臺就需要創建一個新線程來處理,下次新任務來了肯定又要創建新線程處理的, 創 建新線程的開銷是很大的,并且請求過多時,肯定會產生大量的線程出來,這樣會嚴重影響系統的性能。

????????創建線程池:

方式一:使用ExecutorService的實現類ThreadPoolExecutor自創建一個線程池對象。

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)//使用指定的初始化參數創建一個新的線程池對象// 創建線程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(3, // 線程池的核心線程數量10, // 線程池的最大線程數量60, // 臨時線程存活時間TimeUnit.SECONDS, // 臨時線程存活時間單位new ArrayBlockingQueue<>(20), // 任務隊列Executors.defaultThreadFactory(), // 線程工廠new ThreadPoolExecutor.AbortPolicy() // 拒絕策略
);

????????參數一:corePoolSize : 指定線程池的核心線程的數量。

????????參數二:maximumPoolSize:指定線程池的最大線程數量。

????????參數三:keepAliveTime :指定臨時線程的存活時間。 正式工:3 最大員工數:5 臨時工:2 臨時工空閑多久被開除

????????參數四:unit:指定臨時線程存活的時間單位(秒、分、時、天)

????????參數五:workQueue:指定線程池的任務隊列。 客人排隊的地方

????????參數六:threadFactory:指定線程池的線程工廠。 負責招聘員工的(hr)

????????參數七:handler:指定線程池的任務拒絕策略(線程都在忙,任務隊列也滿了的時候,新任務來了該怎么處理)

方式二:使用Executors(線程池的工具類)調用方法返回不同特點的線程池對象。

poolExecutor.execute();
import java.util.List;
import java.util.concurrent.*;/*** void execute(Runnable command) 執行 Runnable 任務* Future<T> submit(Callable<T> task) 執行 Callable 任務,返回未來任務對象,用于獲取線程返回的結果* void shutdown() 等全部任務執行完畢后,再關閉線程池!* List<Runnable> shutdownNow() 立刻關閉線程池,停止正在執行的任務,并返回隊列中未執行的任*/
public class Demo2 {public static void main(String[] args) {// 創建線程池ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(3,10,60,TimeUnit.HOURS,new ArrayBlockingQueue<>(20),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());for (int i = 1; i <= 16; i++) {/*** 創建線程* 新任務提交時發現核心線程都在忙,任務隊列也滿了,* 并且還可以創建臨時線程,此時才會創建臨時線程** 核心線程和臨時線程都在忙,任務隊列也滿了(最大線程數10+隊列數20)* 新的任務過來的時候才會開始拒絕任務。**/// 創建線程, 執行Runnable任務poolExecutor.execute(new Student("小紅"+i));// 創建線程, 執行Callable任務poolExecutor.execute(new FutureTask<>(new Student2("張三"+i)));}// 關閉線程池poolExecutor.shutdown();}}class Student implements Runnable{private String name;public Student(String name){this.name=name;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"在教:"+name+"學游泳");}
}class Student2 implements Callable {private String name;public Student2(String name){this.name=name;}@Overridepublic Object call() throws Exception {System.out.println(Thread.currentThread().getName()+"在教:"+name+"學游泳");return null;}
}

任務拒絕策略:

ThreadPoolExecutor.AbortPolicy()
丟棄任務并拋出RejectedExecutionException異常。是默認的策略ThreadPoolExecutor. DiscardPolicy()
丟棄任務,但是不拋出異常,這是不推薦的做法ThreadPoolExecutor. DiscardOldestPolicy()
拋棄隊列中等待最久的任務 然后把當前任務加入隊列中ThreadPoolExecutor. CallerRunsPolicy()
由主線程負責調用任務的run()方法從而繞過線程池直接執行

并發/并行:

????????并發的含義 :進程中的線程是由CPU負責調度執行的,但CPU能同時處理線程的數量有限,為了保證全部線程都能往前執行, CPU會輪詢為系統的每個線程服務,由于CPU切換的速度很快,給我們的感覺這些線程在同時執行,這就是并發。

????????并行的理解: 在同一個時刻上,同時有多個線程在被CPU調度執行。


#學無止盡? ? #記錄并分享我的學習日常

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

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

相關文章

Github 2025-04-19Rust開源項目日報 Top10

根據Github Trendings的統計,今日(2025-04-19統計)共有10個項目上榜。根據開發語言中項目的數量,匯總情況如下: 開發語言項目數量Rust項目10Python項目1Rust: 構建可靠高效軟件的開源項目 創建周期:5064 天開發語言:Rust協議類型:OtherStar數量:92978 個Fork數量:12000…

OpenLayers:視圖變換的方法

一、什么是視圖變換&#xff1f; 視圖變換就是指視圖的 extent&#xff08;范圍&#xff09;、center&#xff08;中心點&#xff09;、zoom&#xff08;縮放級別&#xff09;、 resolution&#xff08;分辨率&#xff09;、rotation&#xff08;旋轉角&#xff09;等參數發生…

數字孿生火星探測車,星際探索可視化

運用圖撲構建數字孿生火星探測車&#xff0c;高精度還原外觀與內部構造。實時映射探測車在火星表面的移動、探測作業及設備狀態&#xff0c;助力科研人員遠程監測、分析數據&#xff0c;為火星探索任務提供可視化決策支持。

【NLP 66、實踐 ? 基于Agent + Prompt Engineering文章閱讀】

你用什么擦干我的眼淚 莎士比亞全集 工業紙巾 還是你同樣泛紅的眼睛 —— 4.19 一、?【核心函數】定義大模型調用函數 call_large_model prompt&#xff1a;用戶傳入的提示詞&#xff08;如 “請分析這篇作文的主題”&#xff09;&#xff0c;指導模型執行任務 client&…

黑馬Java基礎筆記-1

JVM&#xff0c;JDK和JRE JDK是java的開發環境 JVM虛擬機&#xff1a;Java程序運行的地方 核心類庫&#xff1a;Java已經寫好的東西&#xff0c;我們可以直接用。 System.out.print中的這些方法就是核心庫中的所包含的 開發工具: javac&#xff08;編譯工具&#xff09;、java&…

PR第一課

目錄 1.新建 2.PR內部設置 3.導入素材 4.關于素材窗口 5.關于編輯窗口 6.序列的創建 7.視頻、圖片、音樂 7.1 帶有透明通道的素材 8.導出作品 8.1 打開方法 8.2 導出時&#xff0c;需要修改的參數 1.新建 2.PR內部設置 隨意點開 編輯->首選項 中的任意內容&a…

Xcode16 調整 Provisioning Profiles 目錄導致證書查不到

cronet demo 使用的 ninja 打包&#xff0c;查找 Provisioning Profiles 路徑是 ~/Library/MobileDevice/Provisioning Profiles&#xff0c;但 Xcode16 把該路徑改為了 ~/Library/Developer/Xcode/UserData/Provisioning Profiles&#xff0c;導致在編譯 cronet 的demo 時找不…

【更新完畢】2025華中杯C題數學建模網絡挑戰賽思路代碼文章教學數學建模思路:就業狀態分析與預測

完整內容請看文末最后的推廣群 先展示文章和代碼、再給出四個問題詳細的模型 基于多模型下的就業狀態研究 摘要 隨著全球經濟一體化和信息技術的迅猛發展&#xff0c;失業問題和就業市場的匹配性問題愈加突出。為了解決這一問題&#xff0c;本文提出了一種基于統計學習和機器學…

[HOT 100] 1964. 找出到每個位置為止最長的有效障礙賽跑路線

文章目錄 1. 題目鏈接2. 題目描述3. 題目示例4. 解題思路5. 題解代碼6. 復雜度分析 1. 題目鏈接 1964. 找出到每個位置為止最長的有效障礙賽跑路線 - 力扣&#xff08;LeetCode&#xff09; 2. 題目描述 你打算構建一些障礙賽跑路線。給你一個 下標從 0 開始 的整數數組 obst…

2025年KBS SCI1區TOP:增強天鷹算法EBAO,深度解析+性能實測

目錄 1.摘要2.天鷹算法AO原理3.改進策略4.結果展示5.參考文獻6.代碼獲取 1.摘要 本文提出了增強二進制天鷹算法&#xff08;EBAO&#xff09;&#xff0c;針對無線傳感器網絡&#xff08;WSNs&#xff09;中的入侵檢測系統&#xff08;IDSs&#xff09;。由于WSNs的特點是規模…

JavaScript數據類型簡介

在JavaScript中&#xff0c;理解不同的數據類型是掌握這門語言的基礎。數據類型決定了變量可以存儲什么樣的值以及這些值能夠執行的操作。JavaScript支持多種數據類型&#xff0c;每種都有其特定的用途和特點。本文將詳細介紹JavaScript中的主要數據類型&#xff0c;并提供一些…

性能比拼: Elixir vs Go(第二輪)

本內容是對知名性能評測博主 Anton Putra Elixir vs Go (Golang) Performance Benchmark (Round 2) 內容的翻譯與整理, 有適當刪減, 相關指標和結論以原作為準 這是第二輪關于 Elixir 和 Go 的對比測試。我收到了一份來自 Elixir 創作者的 Pull Request &#xff0c;并且我認為…

接口自動化 ——fixture allure

一.參數化實現數據驅動 上一篇介紹了參數化&#xff0c;這篇 說說用參數化實現數據驅動。在有很多測試用例的時候&#xff0c;可以將測試用例都存儲在文件里&#xff0c;進行讀寫調用。本篇主要介紹 csv 文件和 json 文件。 1.讀取 csv 文件數據 首先創建 csv 文件&#xff…

`peft`(Parameter-Efficient Fine-Tuning:高效微調)是什么

peft(Parameter-Efficient Fine-Tuning:高效微調)是什么 peft庫是Hugging Face推出的用于高效參數微調的庫,它能在不調整模型全部參數的情況下,以較少的可訓練參數對預訓練模型進行微調,從而顯著降低計算資源需求和微調成本。以下從核心功能、優勢、常見微調方法、使用場…

編程常見錯誤歸類

上一篇講了調試&#xff0c;今天通過一個舉例回憶一下上一篇內容吧&#xff01; 1. 回顧&#xff1a;調試舉例 在VS2022、X86、Debug的環境下&#xff0c;編譯器不做任何優化的話&#xff0c;下?代碼執?的結果是啥&#xff1f; #include <stdio.h> int main() {int …

在windows上交叉編譯opencv供RK3588使用

環境 NDK r27、RK3588 安卓板子、Android 12 步驟操作要點1. NDK 下載選擇 r27 版本&#xff0c;解壓到無空格路徑&#xff08;如 C:/ndk&#xff09;2. 環境變量配置添加 ANDROID_NDK_ROOT 和工具鏈路徑到系統 PATH3. CMake 參數調整指定 ANDROID_NATIVE_API_LEVEL31、ANDRO…

淺析MySQL事務鎖

在 MySQL 中,事務鎖是用于確保數據一致性和并發控制的重要機制。事務鎖可以幫助防止多個事務同時修改同一數據,從而避免數據不一致和臟讀、不可重復讀、幻讀等問題。 以下是 MySQL 事務鎖的關鍵點總結: 事務鎖:用于確保數據一致性和并發控制。鎖的類型: 行級鎖:InnoDB,粒…

vue3 文件下載(excel/rar/zip)

安裝axios npm install axios 在項目中引入 import axios from axios; 1、get接口excel文件下載 const file_key ref() const downLoadExcel (value:any) > {//file_key.value value axios({method: "get",url: "/api/da/download_excel/",//url:…

RT-Thread RTThread studio 初使用

RT-Thread Studio 下載 https://www.rt-thread.org/studio.html 安裝使用 https://bbs.elecfans.com/jishu_2425653_1_1.html 4 編譯問題解決 問題一&#xff1a;error: unknown type name clock_t 具體的類型值是在sys/_types.h中定義的&#xff0c;需要包含sys/_types.h 這個…

漢諾塔專題:P1760 通天之漢諾塔 題解 + Problem D: 漢諾塔 題解

1. P1760 通天之漢諾塔 題解 題目背景 直達通天路小A歷險記第四篇 題目描述 在你的幫助下&#xff0c;小 A 成功收集到了寶貴的數據&#xff0c;他終于來到了傳說中連接通天路的通天山。但是這距離通天路仍然有一段距離&#xff0c;但是小 A 突然發現他沒有地圖&#xff0…