【線程安全問題的原因和方法】【java形式】【圖片詳解】

在本章節中采用實例+圖片的方式,以一個學習者的姿態進行描述問題+解決問題,更加清晰明了,以及過程中會發問的問題都會一一進行呈現

目錄

  • 線程安全
      • 演示線程不安全情況
        • 圖片解釋:
      • 將上述代碼進行修改【從并行轉化成穿行的方式】
      • 不會出現問題的可能
      • 埋坑問題
    • 總結(線程安全問題產生原因)
  • 如何解決線程安全問題
    • 1.根本原因:
    • 2.多線程同時修改一個變量
    • 3.修改操作,不是原子的
    • 注意:

線程安全

概念:一段代碼在多線程并發執行的情況下,出現bug的情況(實際結果與預期結果不符合),預期結果:一般是由別人來預期,此時這種情況是“線程不安全”

演示線程不安全情況

eg:(如果我們需要計算一個20000;兩個線程每個線程執行10000次看其的一個情況)

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

最終運行的結果為:
在這里插入圖片描述

對代碼進行解釋:
其中
Thread t1 = new Thread(()->{
for (int i = 0; i < 10000; i++) {
count++;
}
});
對應的是3個cpu的操作:

  1. load:將內存中的count加載到寄存器上
  2. add:把寄存器中的內容進行+1;
  3. save:把寄存器當中的內容保留在內存上

所以最終會出現這樣的情況

圖片解釋:

對上述代碼的圖片解釋
請添加圖片描述

將上述代碼進行修改【從并行轉化成穿行的方式】

//只需要使用join方法就可以將并行執行的方式改為串行的方式
public class Test {public static int count = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 10000; i++) {count++;}});Thread t2 = new Thread(()->{for (int i = 0; i < 10000; i++) {count++;}});t1.start();t1.join();t2.start();t2.join();Thread.sleep(1000);System.out.println(count);}
}

最終的結果為:20000
此時上述的反應就被稱為——“線程不安全”

不會出現問題的可能

  1. 如果線程重復的次數少:其運行的速度非常快,會出現一個線程執行完了,另一個線程還沒有開始執行——結果就是正確的
    eg:
public class Test {public static int count = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{for (int i = 0; i < 10; i++) {count++;}});Thread t2 = new Thread(()->{for (int i = 0; i < 10; i++) {count++;}});t1.start();//t1.join();t2.start();//t2.join();Thread.sleep(1000);System.out.println(count);}
}

此時的結果就是正確的;
由于他的速度非常快,在狀態轉化的過程當中前一個線程就已經完成了操作

埋坑問題

  1. 在上述的代碼當中是否可能出現最終打印的結果<5000
    答:可能會出現
    解釋圖片:
    請添加圖片描述
    在上面的情況中會出現最終打印的1
  2. 那由上面問題 ,是否最終在我們之前寫的代碼中出現打印1的情況
    答:這個是不太可能的
    因為執行的次序是搶占資源的方式,在這個過程中,很少可能會出現4999都只由一個線程執行,然后由兩一個線程進行收尾

總結(線程安全問題產生原因)

  1. 根本原因:操作系統對線程的調度是隨機的,搶占式執行的方式
  2. 多個線程同時修改同一個變量
    以下是不會出現問題的情況:
    一個線程修改一個變量
    多個線程不同時修改同一個變量
    多個線程修改不同變量
    多個線程讀取同一變量
  3. 修改操作不是原子的

進行解釋:如果修改操作只對應一個cpu質量——此時原子的(例如沒有多線程的時候,java當中的main線程就不會發生任何的問題)

如何解決線程安全問題

將他產生的原因盡行打破

1.根本原因:

這個是操作系統底層的設定,不能進行改變
或者自己寫一個操作系統:兩大難點1》技術上會非常難2》推廣上會更加難

2.多線程同時修改一個變量

解決方法:調整代碼結構【但是這種方法并不是通用的】

3.修改操作,不是原子的

這個是java當中解決線程安全問題,最主要的解決方案
加鎖

關鍵字:synchronized
synchronized(加鎖對象){——加鎖操作
執行相關代碼
}——解鎖操作

加鎖操作,不是將整個線程鎖死在cpu上,禁止這個線程被其他的調度走,但是禁止其他線程重新加這個鎖,避免其線程成的操作

java當中可以使用這個關鍵字修飾任何對象
只有兩個線程針對同一個對象加鎖操作,才會產生互斥效果

鎖對象并不會影響這個對象其他方面的使用
結果展示:

public class Test2 {public static int count = 0;public static void main(String[] args) throws InterruptedException {Object o = new Object();Thread t1 = new Thread(()->{synchronized (o) {for (int i = 0; i < 10; i++) {count++;}}});Thread t2 = new Thread(()->{synchronized (o) {for (int i = 0; i < 10; i++) {count++;}}});t1.start();//t1.join();t2.start();//t2.join();Thread.sleep(1000);System.out.println(count);}
}

上述代碼的最終的結果就可以被正確打印
對代碼進行的解釋:
請添加圖片描述

注意:

在此過程中,只有針對同一個對象加鎖才會有效果
在一般情況下:synchronized是對this進行加鎖
當synchronized修飾static修飾的變量時,相當于針對類對象盡心的加鎖操作

下一張會講到死鎖的問題,不要走開哦!!!!

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

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

相關文章

Infinite you:flexible photo recrafting while preserving your identity

基于DiT的id保留圖像生成面臨著多種挑戰,缺乏定制模塊設計,模型擴展的困難以及高質量數據的匱乏,因此基于flux的解決方案是相對稀缺的,pulid-flux是基于flux的id保留的初步嘗試,包括instantx和xlabs-ai的flux.1-dev ip-adapters,現有方法在三個關鍵方面保險不足:1.身份相…

Unity 實現一個簡易可拓展性的對話系統

本人能力有限,一切實現僅供參考,如有不足還請斧正 起因是我看到學校社團內有人做了對話系統的分享,我想了想之前沒寫過這種東西,而Fungus插件教程太老了,NodeCanvas插件學習成本又比較高,我就干脆尋找資料 加上自己迭代一下,花了一天時間完成了這個對話系統 目錄 1.介紹 2.核…

linux常用指令(6)

今天我們繼續學習一些linux常用指令,豐富我們linux基礎知識,那么話不多說,來看. 1.cp指令 功能描述&#xff1a;拷貝文件到指定目錄 基本語法&#xff1a;cp [選項] source dest 常用選項&#xff1a;-r&#xff1a;遞歸復制整個文件夾 拷貝文件&#xff1a; 拷貝文件夾&am…

Vue 3 中的路由傳參詳解※※※※

前言 在Vue應用中&#xff0c;路由傳參是非常常見的需求&#xff0c;它允許我們在不同的組件之間傳遞數據。Vue Router提供了兩種主要的方式來傳遞參數&#xff1a;query參數和params參數。下面我們將詳細探討這兩種傳參方式的使用方法和注意事項。 一、query參數 Query參數…

如何創建一個socket服務器?

1. 導入必要的庫 首先&#xff0c;需要導入Python的socket庫&#xff0c;它提供了創建和管理socket連接的功能。 python import socket 2. 創建服務器端socket 使用socket.socket()函數創建一個服務器端的socket對象&#xff0c;指定協議族&#xff08;如socket.AF_INET表示…

lua垃圾回收

lua垃圾回收 lua 垃圾回收 lua 垃圾回收 collectgarbage(“count”)獲取當前lua腳本占用內存字節數(單位為KB)。 collectgarbage(“collect”)執行一次垃圾回收。 xxxnil 將變量置為空&#xff0c;會釋放內存。 lua中的機制和c#中回收機制很類似 解除羈絆(置為空)。 --垃圾回…

友思特應用 | 行業首創:基于深度學習視覺平臺的AI驅動輪胎檢測自動化

導讀 全球領先的輪胎制造商 NEXEN TIRE 在其輪胎生產檢測過程中使用了基于友思特伙伴Neurocle開發的AI深度學習視覺平臺&#xff0c;實現缺陷檢測率高達99.96%&#xff0c;是該行業首個使用AI平臺技術推動缺陷檢測自動化流程的企業。 將AI應用從輪胎開發擴展到制造過程 2024年…

前后端+數據庫的項目實戰:hbu迎新網-較復雜(下)javaweb

目錄 十一、實現對內容的富文本編輯&#xff08;換行、圖片顏色等等樣式&#xff09; &#xff08;1&#xff09;下載富文本編輯器&#xff0c;引入資源 &#xff08;2&#xff09;將原項目的內容部分替換為富文本編輯器 1、替換添加頁面 2、替換修改頁面&#xff08;和添…

腳本語言 Lua

概念 Lua由標準C編寫而成&#xff0c;幾乎在所有操作系統和平臺上都可以編譯、運行。Lua腳本可以很容易地被C/C 代碼調用&#xff0c;也可以反過來調用C/C的函數&#xff0c;這使得Lua在應用程序中可以被廣泛應用。Lua并沒有提供強大的庫&#xff0c;它是不適合作為開發獨立應…

【數據分享】2000—2024年我國鄉鎮的逐月歸一化植被指數(NDVI)數據(Shp/Excel格式)

之前我們分享過2000—2024年我國省市縣三級逐月歸一化植被指數&#xff08;NDVI&#xff09;數據&#xff0c;該數據是基于NASA定期發布的MOD13A3數據集中的月度NDVI柵格數據&#xff08;可查看之前的文章獲悉詳情&#xff09;計算得出。很多小伙伴拿到數據后反饋是否可以處理出…

【負載均衡系列】HAProxy

HAProxy(High Availability Proxy)是一款高性能的 ?TCP/HTTP 負載均衡器,專注于提供高可用性、靈活性和可靠性。以下是關于HAProxy的詳細解析,涵蓋其工作原理、工作機制、工作模式等核心方面: 一、HAProxy 工作原理 HAProxy的核心職責是將客戶端請求高效、可靠地分發到后…

輕松遷移 Elasticsearch 數據:如何將自建索引導出并導入到另一個實例

概述 在日常的 Elasticsearch 運維和數據管理中&#xff0c;數據遷移是一個常見的需求。無論是為了備份、升級&#xff0c;還是將數據從一個集群遷移到另一個集群&#xff0c;導出和導入索引數據都是至關重要的操作。本文將詳細介紹如何將自建 Elasticsearch 實例中的索引數據…

JVM 類加載器之間的層次關系,以及類加載的委托機制

JVM 類加載器之間存在一種層次關系&#xff0c;通常被稱為雙親委派模型 (Parent Delegation Model)。這種層次關系和委托機制是 Java 類加載機制的核心&#xff0c;對于保證 Java 程序的安全性和避免類沖突至關重要。 1. 類加載器的層次關系: JVM 中的類加載器&#xff08;Cl…

基于 Vue 3 的PDF和Excel導出

以下是基于 Vue 3 Composition API 的完整實現&#xff0c;包括 PDF 和 Excel 導出。 一、PDF 導出 (Vue 3) 安裝依賴 在項目中安裝相關庫&#xff1a; npm install html2canvas jspdf Vue 3 代碼實現 <template><div><div ref"pdfContent" cla…

【Jupyter】notebook無法顯示tqdm進度條

錯誤描述 from tqdm.notebook import tqdm 用的時候報錯&#xff1a; Error displaying widget解決方式 # 先裝nodejs conda install -c conda-forge nodejs20# 重裝ipywidgets pip uninstall ipywidgets pip install ipywidgets jupyter labextension install jupyter-wid…

ubuntu20如何升級nginx到最新版本(其它版本大概率也可以)

前言&#xff1a; Nginx非常常用&#xff0c;所以在網絡安全方面備受“關注”。其漏洞非常多&#xff0c;要經常保持軟件更新版本才能更好的保證安全。但是Ubuntu官網適配nginx非常慢&#xff0c;所以nginx官方也會推出針對主流Linux操作系統的包管理工具安裝方式。 步驟&…

word插入Mathtype公式居中和自動更新

word插入公式自動更新 前提&#xff1a;安裝Mathtype 1.word中查看頁的寬度 出現如下 2.設置樣式 出現這個窗口 給樣式隨便起個名字 3.修改樣式 3.1 設置兩個制表位 第二個 3.2 修改公式字體 如下所示 4. 修改公式格式 4.1在word中打開 Mathtype 4.2 修改公式的格式 變成…

如何從后端實現頁面跳轉?

例&#xff1a;請求轉發 例&#xff1a;重定向 例&#xff1a;區別&#xff1a;攜帶參數的后端跳轉 例&#xff1a;是否可以訪問外部資源 請求轉發&#xff1a;客戶端發起一個請求到服務端&#xff0c;服務端把這個請求轉發至其他地方 重定向&#xff1a;客戶端發起一個請求…

APIJSON快速入門

作者 版本 時間 內容 備注 Allen V1.0.0 2021/08/19 初稿完成 AllenV1.0.1 2021/08/22 添加常見問題 1.流程說明 一個接口的開發,比如Java用SpringBoot,Mybatis來開發一般來說就像下面這個流程 部署上這個項目后,流程變成了這樣 如果使用 apijson-framework,還可進一步簡化…

STM32八股【3】------RAM和片上FLASH

1、RAM和FLASH構成 1.RAM ┌──────────────────────────┐ │ 棧區 (Stack) │ ← 從RAM頂端向下擴展&#xff08;存儲局部變量、函數調用信息&#xff09; │--------------------------│ │ 堆區 (Heap) │ ← …