Java--多線程基礎知識(2)

一.多線程的中斷

1.通過自定義的變量來作為標志位

import java.util.Scanner;public class Demo1 {public static boolean flg = false;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while (!flg){System.out.println("t1線程正在執行");try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("t1線程被中斷");}}System.out.println("t1線程停止");});t1.start();Scanner scanner = new Scanner(System.in);scanner.next();flg = true;}
}

在代碼中,我們將 flg 作為標志位,當代碼運行的時候我們需要輸入任意鍵為就可以改變 flg,以此來達到停止代碼運行的作用;

注意:flg 必須作為全局變量,因為 lambda 捕獲的局部變量是不能被修改的,這是為了確保代碼的安全性和一致性,由于我們要對flg 進行修改,所以我們要將 flg 作為全局變量。

2.使用 Thread.interrupted() 或者

Thread.currentThread.isInterrupted()代替標志位

import java.util.Scanner;public class Demo2 {public static void main(String[] args) {Thread t1 = new Thread(()->{Thread cur = Thread.currentThread();while (!cur.isInterrupted()){System.out.println("t1線程正在執行");try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("t1線程被中斷");}}System.out.println("t1線程停止");});t1.start();Scanner scanner = new Scanner(System.in);scanner.next();t1.interrupt();}
}

以上代碼我們將 cur.isInterrupted 作為標志位,當我們輸入任意字符之后就會調用 t1.interrupt() 方法,將標志位改為 false,但是運行后并且輸入后我們發現代碼仍在執行,

Java的線程中斷機制有這樣的設計:

當一個線程在阻塞狀態(Thread.join,Thread.sleep,Thread.wait)下被中斷時,中斷標志位會被自動清除,也就是說當調用 Thread.interrupt() 之后,雖然? !cur.isInterrupted() 改為了 false,但是由于被清除了標志位,又變為了 true,這才導致程序仍然在運行。

也就是說 interrupt 方法做了兩件事:首先將標志位設置為了 true,但是由于喚醒了 sleep ,以異常的方式返回了,清除了線程的標志位,將標志位重新設置為了 false。針對這種情況,我們需要在 catch 中做更詳細的處理。

二.線程的等待與安全問題

public class Demo3 {static int result = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {result++;}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {result++;}});t1.start();t2.start();Thread.sleep(1000);System.out.println( result);}
}

以上代碼我們創建了兩個線程 : 分別是 t1,t2,這兩個線程同時對 result ++,正常情況下,result 的結果肯定是100000的,但在我們運行代碼后:

我們發現并沒有到達100000,這就是多線程引發的線程安全問題,線程對 result++ 要進行三個操作:首先將 result 從主內存中讀取到工作內存中,其次將 result 在工作內存中++,最后將result的值放回到主內存中,問題就出在這幾步上,由于操作不是原子的,導致在 t1 從主內存中讀取到 result的值并且++之后,t2也進行了從主內存中讀取到了 result 的值,此時t1 和t2都需要將工作內存中的值寫入到主內存當中,這樣就會導致其中一個線程的值會覆蓋掉另一個線程的值,這才導致了 result 的值并沒有到達100000;

針對這個問題:

我們可以讓某一個線程通過 Thread.sleep() 來等待另一個線程運行,但這種方法我們無法確定另一個線程運行多久,所以不推薦使用。

第二個方法是運行 join() 方法,

public class Demo3 {static int result = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {result++;}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {result++;}});t1.start();t1.join();t2.start();Thread.sleep(1000);System.out.println( result);}
}

在 t2 運行之前調用 t1.join() 方法,此時main線程會等待 t1 線程運行完成之后再往下走調用 t2.start,此時就解決了上述問題。

三.線程的狀態

線程的狀態分為六種:

new,runnable,terminated 狀態演示:

public class Demo4 {public static void main(String[] args) {Thread t1 = new Thread(()->{for (int i = 0; i < 100; i++) {}});System.out.println(t1.getState());t1.start();System.out.println(t1.getState());try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(t1.getState());}}

timed_waiting , blocked 狀態演示:

public class Demo5{static Object object = new Object();public static void main(String[] args) {Thread t1 = new Thread(()->{synchronized( object){try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread t2 = new Thread(()->{synchronized ( object){while ( true){}}});t1.start();t2.start();try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(t1.getState());System.out.println(t2.getState());}
}

將上述部分代碼進行更改,就可以得到 waiting 狀態:

        Thread t1 = new Thread(()->{synchronized( object){try {object.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}});

四.線程的鎖

還是對上述的線程安全問題進行解決,線程安全問題其中的導火索之一便是代碼的非原子性,由于一件事情要分幾步來操作,這種情況容易出問題,所以就出現了鎖,鎖可以將幾個步驟的操作綁定在一起,這樣就可以變相的實現代碼的原子化。

public class Demo6 {static int result = 0;static Object locker = new Object();public static void add(int a){synchronized (locker){result++;}}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 50000; i++) {add(result);}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {add(result);}});t1.start();t2.start();Thread.sleep(100);System.out.println( result);}
}

sychronized(Object object){ }

其中 sychronized 就是鎖的關鍵字,他需要的參數是 Object 類,就像是對 object 上了鎖,其他人想要進是沒有辦法的,除非你釋放了鎖,當你走完{ }的時候,此時就完成了對鎖的釋放。

上述代碼我們對 add類里面的內容上了鎖,我們還可以對線程的內容直接加鎖:

public class Demo6 {static int result = 0;static Object locker = new Object();public static void add(int a){result++;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{synchronized(locker){for (int i = 0; i < 50000; i++) {add(result);}}});Thread t2 = new Thread(()->{synchronized(locker){for (int i = 0; i < 50000; i++) {add(result);}};});t1.start();t2.start();Thread.sleep(100);System.out.println( result);}
}

運行結果不變;

要注意:我們不能只對一個線程加鎖,那樣的話是沒有意義的:

public class Demo6 {static int result = 0;static Object locker = new Object();public static void add(int a){result++;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{synchronized(locker){for (int i = 0; i < 50000; i++) {add(result);}}});Thread t2 = new Thread(()->{for (int i = 0; i < 50000; i++) {add(result);};});t1.start();t2.start();Thread.sleep(100);System.out.println( result);}
}

這是因為如果我們只對一個線程的內容上鎖了,另一個線程沒有對同一個Object類上鎖,兩個線程還是會各自運行各自的。

最后就是:兩個線程針對不同的 Object 類上鎖也會有線程安全問題:

public class Demo6 {static int result = 0;static Object locker = new Object();static Object locker2 = new Object();public static void add(int a){result++;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{synchronized(locker){for (int i = 0; i < 50000; i++) {add(result);}}});Thread t2 = new Thread(()->{synchronized(locker2){for (int i = 0; i < 50000; i++) {add(result);};}});t1.start();t2.start();Thread.sleep(100);System.out.println( result);}
}

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

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

相關文章

Qit_計網筆記

第1章 概述1.1 計算機網絡在信息時代中的作用一、計算機網絡基礎概念&#xff08;一&#xff09;計算機網絡的定義定義&#xff1a;計算機網絡在信息時代中起到核心作用&#xff0c;實現了萬物聯網和人人用網的目標。&#xff08;二&#xff09;計算機網絡的特點信息時代特征&a…

【C++11】initializer_list列表初始化、右值引用和移動語義、可變參數模版等

目錄 前言 一、簡介一下C11 二、{}列表初始化 三、右值引用和移動語義 四、右值引用和移動語義的使用場景 五、右值引用和移動語義在傳參中的提效 六、引用折疊和完美轉發 七、可變參數模板 前言 本文主要介紹C11中新增的一些重要語法&#xff1a;包括initializer_list列表初…

MP3 ID3標簽中的數字流派代碼和文本值翻譯成的中文列表

將MP3 ID3標簽中的數字流派代碼和文本值翻譯成的中文列表&#xff1a;■ 數字代碼流派:0 布魯斯 (Blues)1 古典搖滾 (Classic Rock)2 鄉村音樂 (Country)3 舞曲 (Dance)4 迪斯科 (Disco)5 放克 (Funk)6 垃圾搖滾 (Grunge)7 嘻哈 (Hip-Hop)8 爵士樂 (Jazz)9 金屬樂 (M…

U8g2庫為XFP1116-07AY(128x64 OLED)實現菜單功能[ep:esp8266]

使用U8g2庫為XFP1116-07AY&#xff08;128x64 OLED&#xff09;實現菜單功能&#xff0c;核心是通過按鍵控制菜單切換、光標移動和選項選中&#xff0c;結合U8g2的繪圖/文本函數實現交互邏輯支持多級菜單&#xff08;主菜單→子菜單→功能執行&#xff09;&#xff0c;并兼容ES…

easy-dataset 框架綜合技術分析:面向領域特定 LLM 指令數據的合成

摘要 本報告對 easy-dataset 框架 進行全面技術剖析&#xff0c;該框架旨在解決大型語言模型&#xff08;LLM&#xff09;在特定領域應用中的核心瓶頸——高質量指令微調數據的稀缺性。隨著 LLM 技術發展&#xff0c;其應用能力不再僅依賴模型參數規模&#xff0c;而是更依賴通…

【開題答辯全過程】以 4s店汽車銷售系統為例,包含答辯的問題和答案

個人簡介一名14年經驗的資深畢設內行人&#xff0c;語言擅長Java、php、微信小程序、Python、Golang、安卓Android等開發項目包括大數據、深度學習、網站、小程序、安卓、算法。平常會做一些項目定制化開發、代碼講解、答辯教學、文檔編寫、也懂一些降重方面的技巧。感謝大家的…

測試中的Bug

文章目錄軟件測試的生命周期軟件測試的各個階段線上環境測試中的BUG描述測試BUGBUG的級別為啥要定義BUG的級別&#xff1f;BUG有哪些級別呢&#xff1f;BUG的生命周期測試與開發發生爭執怎么辦&#xff1f;測試與開發會發生啥爭執&#xff1f;為啥會發生這樣的爭執&#xff1f;…

aws共享一個鏡像并有畫圖功能

這樣可以方便的把系統安裝好&#xff0c;不會重復勞動了。 這個是frequi 單獨安裝 wget https://github.com/freqtrade/frequi/releases/download/2.0.7/freqUI.zip freqtrade install-ui pip install -U -r requirements-plot.txt 在AWS上把已經安裝好的環境共享給其他用戶。…

C語言---goto語句

文章目錄基本語法代碼示例goto 的常見用途&#xff08;盡管不推薦&#xff09;為什么 goto 聲名狼藉&#xff1f;&#xff08;goto的缺點&#xff09;如何避免使用 goto&#xff1f;&#xff08;替代方案&#xff09;goto 語句是一種無條件跳轉語句&#xff0c;它用于將程序的控…

Flask框架的簡單了解

&#x1f91f;致敬讀者 &#x1f7e9;感謝閱讀&#x1f7e6;笑口常開&#x1f7ea;生日快樂?早點睡覺 &#x1f4d8;博主相關 &#x1f7e7;博主信息&#x1f7e8;博客首頁&#x1f7eb;專欄推薦&#x1f7e5;活動信息 文章目錄1. 前言2. 簡介3. 核心特點4. 代碼實例5. 主要…

——貪心算法——

目錄 1 檸檬水找零 2 將數組和減半的最少操作次數 3 最大數 4 擺動序列 5 最長遞增子序列 6 遞增的三元子序列 7 最長連續遞增序列 8 買賣股票的最佳時機 9 買賣股票的最佳時機 II 10 K 次取反后最大化的數組和 11 按身高排序 12 優勢洗牌 13 最長回文串 14 增減…

網絡操作系統與分布式操作系統的區別

網絡操作系統與分布式操作系統的區別架構設計網絡操作系統&#xff08;NOS&#xff09;基于客戶端-服務器模型&#xff0c;通過共享資源&#xff08;如文件、打印機&#xff09;提供服務&#xff0c;各節點保留獨立的管理和數據處理能力。分布式操作系統&#xff08;DOS&#x…

RabbitMQ—運維篇

RabbitMQ安裝 RabbitMQ需要依賴erlang&#xff0c;如果普通安裝需要安裝erlang并保證二者兼容&#xff0c;因此選擇較為簡單的docker安裝方式 1.獲取rabbitmq鏡像 docker pull rabbitmq:3.11.19-management #rabbitmq-management表示帶有客戶端&#xff08;控制臺&#xff09; …

【學習K230-例程21】GT6700-UDP-Client

B站視頻 UDP 簡介 UDP 是 User Datagram Protocol 的簡稱&#xff0c;中文名是用戶數據報協議&#xff0c;是 OSI&#xff08;Open SystemInterconnection&#xff0c;開放式系統互聯&#xff09;參考模型中一種無連接的傳輸層協議&#xff0c;提供面向事務的簡單不可靠信息傳送…

LazyLLM教程 | 第9講:微調實踐:讓大模型和向量模型更懂你的領域

前面教程中&#xff0c;我們通過優化檢索策略、召回重排略以及基于大模型的查詢重寫策略來提升了RAG系統的檢索精度&#xff0c;但最終回復的結果還需要經過大模型的融合和處理&#xff0c;模型能力的強弱直接影響到最終的結果。這就好比一道好的菜不僅需要有高質量的食材&…

六、vue3后臺項目系列——頁面自適應設計+pinia,vuex的使用

前言&#xff1a;在頁面加入自適應是提高用戶體驗的一種形式&#xff0c;甚至有時候是手機用戶&#xff0c;我們就需要做一個自適應處理&#xff0c;其中肯定會涉及一些狀態條件的判斷&#xff0c;而這些關鍵的條件就是我們用來切換樣式的關鍵&#xff0c;所以我們需要使用狀態…

視頻講解|Python用ResNet殘差神經網絡在大腦出血CT圖像描數據預測應用

全文鏈接&#xff1a;https://tecdat.cn/?p43843 原文出處&#xff1a;拓端抖音號拓端tecdat 分析師&#xff1a;Zikun Zhang 視頻講解Python用ResNet殘差神經網絡在大腦出血CT圖像描數據預測在臨床醫療影像診斷中&#xff0c;大腦出血的快速準確識別直接關系到患者的救治效率…

Mysql中有那些鎖

按照鎖的力度分&#xff1a;1.行級鎖2.表級鎖3.全局鎖4.頁級鎖innodb不支持頁鎖全局鎖全局鎖指的是對整個數據庫實例加鎖&#xff0c;一般用于數據庫的表級鎖表鎖 是對整張表進行加鎖。表級鎖還有以下幾種&#xff1a;意向鎖&#xff1a;意向鎖是指&#xff0c;我們在事務請求表…

基于 CoT 思維鏈協調多 MCP 工具:依托亞馬遜云科技服務打造全流程智能的 Amazon Redshift 運維體系

基于 CoT 思維鏈協調多 MCP 工具&#xff1a;依托亞馬遜云科技服務打造全流程智能的 Amazon Redshift 運維體系 新用戶可獲得高達 200 美元的服務抵扣金 亞馬遜云科技新用戶可以免費使用亞馬遜云科技免費套餐&#xff08;Amazon Free Tier&#xff09;。注冊即可獲得 100 美元的…

手機群控平臺的智能管控技術深度解析

手機群控平臺作為數字化運營的核心工具&#xff0c;正在重塑移動設備管理的技術邊界。其核心價值在于通過集中化控制實現批量化操作&#xff0c;同時借助智能化算法提升管控效率。本文將深入探討其技術架構與實現方案。平臺架構與核心技術手機群控平臺采用分布式架構設計&#…