併發編程學習(2)----volatile與synchronized(待完成)

来源:https://www.cnblogs.com/han02216/archive/2018/03/05/8510501.html
-Advertisement-
Play Games

此次文章主要探討volatile與synchronized,通過一些基礎概念的介紹,讓讀者對於兩者有更深的瞭解。 一、幾個相關概念 1、原子性 其本意是“不能被進一步分隔的最小粒子”,而原子操作意為“不可被中斷的一個或一系列操作”。在多處理器重實現原子操作變得有點複雜。 1)操作系統如何實現原子性。 ...


  此次文章主要探討volatile與synchronized,通過一些基礎概念的介紹,讓讀者對於兩者有更深的瞭解。

一、幾個相關概念

1、原子性

  其本意是“不能被進一步分隔的最小粒子”,而原子操作意為“不可被中斷的一個或一系列操作”。在多處理器重實現原子操作變得有點複雜。

1)操作系統如何實現原子性。

  單處理器可以對同一個緩存行里自動進行16/32/64位的原子操作。但是複雜的記憶體操作處理器是不能保證其原子性的,比如跨匯流排寬度、跨多個緩存行和跨頁表的訪問。例如,i++是一個讀改寫的操作,由於該代碼可能被不同的線程執行導致最終出現的結果可能不是我們想要的結果(具體原因不在此贅述)。但是,處理器提供匯流排鎖定和緩存鎖定兩個機制來保證複雜記憶體操作的原子性。

a。使用匯流排鎖保證原子性

  處理器通過使用匯流排鎖來解決i++問題。所謂匯流排鎖就是使用處理器提供的一個LOCK#信號,當一個處理器在匯流排上輸出此信號時,其他處理器的請求將被阻塞,此時該處理器可以獨占共用記憶體。

b。使用緩存鎖來保證原子性

  匯流排鎖把CPU與記憶體間的通信鎖住了,這使得在鎖定期間,其它處理器不能操作其它記憶體地址的數據,所以匯流排鎖開銷比較大。我們只需要保證對某個記憶體地址的操作是原子性即可(減小鎖粒度)。目前處理器在某些場合下回使用緩存鎖定來代替匯流排鎖來進行優化。

2、可見性

  可見性的意思是當一個線程修改一個共用變數時,另一個線程能讀到這個修改的值。

3、指令重排

  重排序指編譯器和處理器為了優化程式性能而對指令序列進行重新排序的一種手段。在多線程的程式中,對某些指令的重排序可能會改變程式的執行結果(後續會有實例說明)。

二、volatile

1、volatile變數具有以下特性。

  可見性:對一個volatile變數的讀,總是能看到任意線程對這個volatile變數最後的寫入、

  禁止重排序:jdk1.5以後對volatile語義進行了加強,不允許volatile變數之間進行重排序。

2、底層實現原理

1)操作系統層面

  操作系統可通過LOCK#首碼指令實現以上前兩個特性。為了提高處理速度,處理器不直接和記憶體進行通信,而是先將系統記憶體的數據讀到內部緩存(L1、L2或其它)後再進行操作,但操作完不知道何時寫回到記憶體(操作系統這裡其實使用了非同步操作來解決生產消費速度不均的問題)。如果申明瞭volatile的變數進行寫操作,JVM就會想處理器發送一條Lock首碼指令,將這個變數的緩存行的數據寫回到記憶體中。此時,其它處理器中的值還是舊值。在多處理器下,為了保證各個處理器緩存一致,實現了緩存一致性協議。每個處理器通過嗅探在匯流排上傳播的數據來檢查自己緩存的值是不是過期了,當處理器發現自己緩存行過期時,就會將當前處理器的緩存行置為無效狀態,當處理器對這個數據進行修改時,會重新從操作系統記憶體中把數據讀取到處理器緩存中。

  a。Lock首碼指令會引起處理器緩存回寫到記憶體。

  Lock首碼指令導致在執行指令期間,聲言處理器的Lock信號。在多處理器環境下,處理器可以獨占任何共用記憶體。操作系統通過匯流排鎖或者緩存鎖定,來確保同時只能有一個處理器可修改緩存數據。

  b。一個處理器的緩存會寫到記憶體導致其它處理器的緩存無效。

2)JMM層面。

  在Java中,所有實例域、靜態域和數組元素都存儲在堆記憶體中,堆記憶體線上程之間共用。局部變數,方法定義參數和異常處理參數不會線上程之間共用,它們不會有記憶體可見性問題。

  從抽象的角度來看,JMM定義了線程和主記憶體之間的抽象關係:線程之間的共用變數存儲在主記憶體中,每個線程都有一個私有的本地記憶體(Local Memory),本地記憶體中存儲了改線程共用變數的副本。本地記憶體是JMM的一個抽象概念,並不真實存在。JMM的抽象示意圖如下所示。

  當一個變數被申明為volatile時。

  寫入操作:JMM會把該線程對應的本地記憶體中的變數刷新到主存中。

  讀取操作:JMM會把本地記憶體置為無效,線程接下來會從主存中讀取共用變數。

 

        

 

3)禁止重排序應用

  在單例模式中,人們使用了雙重校驗來降低鎖同步的開銷,查看以下無volatile時的代碼。

  

  以上是一個錯誤的優化,當線程執行到第4行時,代碼讀取到instance不為null,但是註意此時instance引用的對象還未初始化。原因如下。

  instance = new Singleton()可以分解為如下3行偽代碼。

    memory = allocate();//1.分配對象的記憶體空間。

    ctorInstance(memory);//2.初始化對象

    instance = memory;// 3.設置instance指向剛剛分配的地址

  步驟2和3由於指令重排,可能導致另一個線程訪問到未被初始化的對象。如果在instance變數前加上volatile即可解決此問題。

 三、synchronized

1、簡單介紹

  synchronized簡單的理解就是對象鎖,Java中的每一個對象都可以作為鎖。它主要可以確保代碼一系列操作在同一線程只能由一個線程訪問。

具體表現為一下3種形式。

  對於普通同步方法,鎖是當前實例對象。

  對於靜態同步方法,鎖是當前的Class對象。

  對於同步方法塊,鎖是synchronized括弧里配置的對象。

2、原理介紹。

  在Java中任意一個對象都擁有自己的監視器,當這個對象由同步塊或者這個對象的同步方法調用時,執行方法的線程必須先獲取到該對象的監視器才能進入到同步塊或者同步方法中,而沒有獲取到監視器的線程將會被阻塞在同步塊或者同步方法入口處,進入BLOCKED狀態。當訪問訪問Object的前驅(獲得了鎖的線程)釋放了鎖,則會喚醒阻塞在同步隊列中的線程,使其重新嘗試對監視器獲取。具體過程如下圖。

         

 

四、synchronized和volatile比較

1、原理分析

  從原理上分析,volatile是JMM藉助操作系統底層指令實現的關鍵字。當變數被它修飾時,它可以保證對於該變數的訪問都需要從共用記憶體中獲取,而每次修改則必須同步刷新回共用記憶體,它能保證所有線程對變數訪問的可見性。synchronized是JVM層面的,它藉助Java對象的monitor實現的,同一時刻只能有一個線程進入監視器,它可以保證程式對於變數訪問的可見性和排它性。

  為了實現線程間的同步,兩者都是通過匯流排鎖/記憶體鎖定、monitor這樣的方式,即線程在同一時刻只能訪問對應的記憶體區域,這樣就避免了多個線程同時寫入記憶體導致結果無法預知的情況。

2、特性對比

1)volatile

  a.可見性:可保證變數在記憶體中的可以性。

  b.多數情況下相對synchronized具有較高的性能

  c.有序性:在某些情況下可以防止指令重排(經典案例為單例雙重校驗)

2)synchronized

  a.性能相對較差,但是1.6以後性能有所提升。

  b.有序性,被加鎖的代碼塊同一時刻只能有一個線程訪問程式,保證程式有序執行

五、總結思考

1)為瞭解決多線程間的同步問題核心思想:通過限定同一時刻僅有一個線程有寫入。

2)操作系統通過減小鎖的粒度提升性能。


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

-Advertisement-
Play Games
更多相關文章
  • Spring security記住我基本原理: 登錄的時候,請求發送給過濾器UsernamePasswordAuthenticationFilter,當該過濾器認證成功後,會調用RememberMeService,會生成一個token,將token寫入到瀏覽器cookie,同時RememberMeS ...
  • 前言: 關於為什麼要引入dubbo框架,而不是用spring cloud或者是motan呢,主要是筆者現在公司用的就是dubbo,並且第一次接觸到微服務的概念是來源於dubbo,再加上最近dubbo頻繁的更新,所以就有採用dubbo改造的想法。建議沒看過這個教程的園友可以先看看原來的教程,因為現在所 ...
  • 時常出差,經常填寫發票,有時網路不好,數數也不會了 於是,一個隨身小應用就這麼誕生 沒錯,我只為了一時的方便~ 有一個數字:1314521.94 我想轉換為大寫 但是它有點長,還有個小數點 於是,我掏出手機 截了一張圖 突然很多人都會了 PS 告訴我你的數字 如果你想將100轉為大寫,只需要發送 金 ...
  • 1、逆向工程的作用 Mybatis 官方提供了逆向工程,可以針對資料庫表自動生成Mybatis執行所需要的代碼(包括mapper.xml、Mapper.java、pojo)。 2、逆向工程的使用方法 逆向工程需要的jar包如下圖所示: ![][1] 也可以直接下載我Github上面的源代碼( "ht ...
  • 前言:有時候無聊看一些搞笑的段子,糗事百科還是個不錯的網站,所以就想用Python來玩一下。也比較簡單,就寫出來分享一下。嘿嘿 環境:Python 2.7 + win7 現在開始,打開糗事百科網站,先來分析。地址:https://www.qiushibaike.com 一般像這種都是文本的話,查看源 ...
  • if __name__=="__main__" 語句在調用多進程Process過程中是必須的,否則會報如下錯誤An attempt has been made to start a new process before the current process has finished its boo... ...
  • python 變數的定義與C語言很不同 C語言的變數使用: int i=6 python的變數使用: i=4 c語言的變數首先要聲明變數的類型,然後對其進行賦值。硬體底層的意義就是在記憶體空間中劃分出一段屬於(變數名)的空間,大小與數據類型有關,接著再放入數據。 這一系列的過程首先保證了安全性,雖然語 ...
  • 對加密文件計算字母個數 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...