jvm-多線程

来源:http://www.cnblogs.com/clovejava/archive/2017/10/14/7669114.html
-Advertisement-
Play Games

多線程的目的 為什麼要使用多線程?可以簡單的分兩個方面來說: 在多個cpu核心下,多線程的好處是顯而易見的,不然多個cpu核心只跑一個線程其他的核心就都浪費了; 即便不考慮多核心,在單核下,多線程也是有意義的,因為在一些操作,比如IO操作阻塞的時候,是不需要cpu參與的,這時候cpu就可以另開一個線 ...


多線程的目的

為什麼要使用多線程?可以簡單的分兩個方面來說:

  • 在多個cpu核心下,多線程的好處是顯而易見的,不然多個cpu核心只跑一個線程其他的核心就都浪費了;
  • 即便不考慮多核心,在單核下,多線程也是有意義的,因為在一些操作,比如IO操作阻塞的時候,是不需要cpu參與的,這時候cpu就可以另開一個線程去做別的事情,等待IO操作完成再回到之前的線程繼續執行即可。

多線程帶來的問題其實多線程根本的問題只有一個:線程間變數的共用

java里的變數可以分3類:

         1類變數(類裡面static修飾的變數)

         2實例變數(類裡面的普通變數)

         3局部變數(方法里聲明的變數)

根據各個區域的定義,我們可以知道:

  1. 類變數 保存在“方法區”
  2. 實例變數 保存在“堆”
  3. 局部變數 保存在 “虛擬機棧”

“方法區”和“堆”都屬於線程共用數據區,“虛擬機棧”屬於線程私有數據區。

因此,局部變數是不能多個線程共用的,而類變數和實例變數是可以多個線程共用的。事實上,在java中,多線程間進行通信的唯一途徑就是通過類變數和實例變數。

也就是說,如果一段多線程程式中如果沒有類變數和實例變數,那麼這段多線程程式就一定是線程安全的。

以Web開發的Servlet為例,一般我們開發的時候,自己的類繼承HttpServlet之後,重寫doPost()、doGet()處理請求,不管我們在這兩個方法里寫什麼代碼,只要沒有操作類變數或實例變數,最後寫出來的代碼就是線程安全的。如果在Servlet類裡面加了實例變數,就很可能出現線程安全性問題,解決方法就是把實例變數改為ThreadLocal變數,而ThreadLocal實現的含義就是讓實例變數變成了“線程私有”的,即給每一個線程分配一個自己的值。

現在我們知道: 其實多線程根本的問題只有一個:線程間變數的共用, 這裡的變數,指的就是類變數和實例變數,後續的一切,都是為瞭解決類變數和實例變數共用的安全問題。

如何安全的共用變數

現在唯一的問題就是要讓多個線程安全的共用變數(下文中的變數一般特指類變數和實例變數),上文提到了一種ThreadLocal的方式,其實這種方式並不是真正的共用,而是為每個線程分配一個自己的值。

比如現在有一個特別簡單的需求,有一個類變數a=0,現在啟動5個線程,每個線程執行a++;如果用ThreadLocal的方式,最後的結果就是5個線程都擁有一份自己的a值,最終結果都是1,這顯然不符合我們的預期。

那麼如果不使用ThreadLocal呢?直接聲明一個類變數a=0,然後讓5個線程分別去執行a++;這樣結果依舊不對,而且結果是不確定的,可能是1,2,3,4,5中的任一個。這種情況叫做競態條件(Race Condition),要理解競態條件先要理解Java記憶體模型:

要理解java的記憶體模型,可以類比電腦硬體訪問記憶體的模型。由於電腦的cpu運算速度和記憶體io速度有幾個數量級的差距,因此現代電腦都不得不加入一層儘可能接近處理器運算速度的高速緩存來做緩衝:將記憶體中運算需要使用的數據先複製到緩存中,當運算結束後再同步回記憶體。如下圖:

因為jvm要實現跨硬體平臺,因此jvm定義了自己的記憶體模型,但是因為jvm的記憶體模型最終還是要映射到硬體上,因此jvm記憶體模型幾乎與硬體的模型一樣:

每個java線程都有一份自己的工作記憶體,線程訪問變數的時候,不能直接訪問主記憶體中的變數,而是先把主記憶體的變數複製到自己的工作記憶體,然後操作自己工作記憶體里的變數,最後再同步給主記憶體。

現在就可以解釋為什麼5個線程執行a++最後結果不一定是5了,因為a++可以分解為3步操作:

  1. 把主記憶體里的a複製到線程的工作記憶體
  2. 線程對工作記憶體里的a執行a=a+1
  3. 把線程工作記憶體里的a同步回主記憶體

而5個線程併發執行的時候完全有可能5個線程都先執行了第一步,這樣5個線程的工作記憶體里a的初始值都是0,然後執行a=a+1後在工作記憶體里的運算結果都是1,最後同步回主記憶體的值肯定也是1。

而避免這種情況的方法就是:在多個線程併發訪問a的時候,保證a在同一個時刻只被一個線程使用。

同步(synchronized)就是:在多個線程併發訪問共用數據的時候,保證共用數據在同一個時刻只被一個線程使用。

同步基本思想

為了保證共用數據在同一時刻只被一個線程使用,我們有一種很簡單的實現思想,就是 在共用數據里保存一個鎖 ,當沒有線程訪問時,鎖是空的,當有第一個線程訪問時,就 在鎖里保存這個線程的標識 並允許這個線程訪問共用數據。在當前線程釋放共用數據之前,如果再有其他線程想要訪問共用數據,就要 等待鎖釋放 。

我們把這種思想的三個關鍵點抽出來:

  1. 在共用數據里保存一個鎖
  2. 在鎖里保存這個線程的標識
  3. 其他線程訪問已加鎖共用數據要等待鎖釋放

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近學了樹狀數組,給我的感覺就是 這個數據結構好神奇啊^_^ 首先她的常數比線段樹小,其次她的實現複雜度也遠低於線段樹 (並沒有黑線段樹的意思 ) 所以熟練掌握她是非常有必要的。。 關於樹狀數組的基礎知識與原理網上一搜一大堆,我就不贅述了,就談一些樹狀數組的應用好了 1,單點修改,求區間和 2,區間 ...
  • 專業團隊定製,【企業品牌推廣】口碑整合,網路營銷,,網站SEO優化,公司網站排名優化等服務。 1、直觀效果 直觀的效果就是百度或者360搜索品牌名稱時出現百科、新聞、問答等相關信息,從而提高品牌可信度、知名度,從新品牌到知名品牌,這是適合的第一期推廣方案;相對而言,也是前期經濟實惠、必不可少的營銷包 ...
  • 對於一個初學者來說for迴圈可能會擊潰很多人,因為網上找的python的for迴圈沒有詳細的介紹for迴圈的條件以及for迴圈後面定義的東西是什麼意思。首先我先舉一個常用的例子。 for i in range(1,10) : print i 上面這兩條代碼是用來在屏幕上列印1到9的。這裡就不對ran ...
  • 在上一章中,我們已經搭建好了struts2的一個開發環境,那麼這一章就來做一個簡單的登錄功能,以及介紹和使用struts2裡面一個重要的東西-通配符。 第一步,在WebContent下麵新建一個login.jsp的頁面,裡面使用form表單實現一個簡單的登錄頁面。 第二步:打開上一章中建好的Logi ...
  • 配置環境 1. 導入jar包,Struts2官網:http://struts.apache.org/ 2. 創建Action類 繼承ActionSupport (ActionSupport類是一個工具類,它已經實現了Action介面。除此之外,它還實現了Validateable介面,提供了數據校驗功 ...
  • 幾周前,我開始工作於一個證券投資組合網站。雖然我只能使用 React 完成整個網站,但我決定使用 Go 來創建一個可以處理某些任務(例如發送 email)的 API 伺服器,相信這是一個很好的做法。我其中的一個頁面是一個 contact 頁面,目前看起來像這樣:我想使用專門為此 contact 表單... ...
  • #include <iostream>#include<cstdio>using namespace std;int main(){ freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); int a,b; while(ci ...
  • 剛看spring3實戰書籍第一章 切麵以前沒有關註過 現在看到了 隨手試驗一下 AOP AOP(Aspect Oriented Programming),即面向切麵編程,可以說是OOP(Object Oriented Programming,面向對象編程)的補充和完善。OOP引入封裝、繼承、多態等概 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...