多線程筆記(二)

来源:https://www.cnblogs.com/xuzhuo123/archive/2022/05/10/16253912.html
-Advertisement-
Play Games

多線程筆記(二) 1. Synchronized 和 Lock 的區別 synchronized是Java的關鍵字,是 JVM 層面的內置功能和實現。 Lock是一個介面,是代碼層面的實現 synchronized可以隱式的獲取,釋放鎖 lock是顯式的獲取,釋放鎖 synchronized在發生異 ...


多線程筆記(二)

1. Synchronized 和 Lock 的區別

  • synchronized是Java的關鍵字,是 JVM 層面的內置功能和實現。

    Lock是一個介面,是代碼層面的實現

  • synchronized可以隱式的獲取,釋放鎖

    lock是顯式的獲取,釋放鎖

  • synchronized在發生異常的時候會自動釋放鎖

    lock在發生異常的時候,不會自動釋放鎖,必須要調用unlock方法才會釋放鎖,否則容易引起死鎖

  • Lock可以嘗試非阻塞獲取鎖,可中斷獲取鎖,超時獲取鎖。

    synchronized並沒有這些功能

2. LockSupport

LockSupport是一個編程工具類,主要是為了阻塞線程(park)和喚醒線程(unpark)時使用

設計原理的核心:許可

​ park:掛起當前線程,等待一個許可

​ unpark:為某個線程提供一個許可,喚醒某個指定的線程

park/unpark和wait/notify很類似,但其具有以下的優點

  • park/unpark是以thread為操作對象,語義更加直觀
  • 操作更為精準和靈活,可以準確的去喚醒某一個線程

park/unpark和wait/notify的區別

wait/notify和synchronized聯繫在一起的,wait過後,線程是進入Blocked狀態

park方法使當前線程掛起,進入到waiting狀態

3. CAS

CAS(Compare And Swap, 比較並替換)中有3個基本的操作數:V:記憶體地址的值; A:舊的預期的值;B:要修改的新的值

基本實現方式:

使用CAS去更新一個變數的時候,只有變數的舊的預期的值A 和記憶體地址的值V 相同的時候,才會將V 修改為新的值B。如果修改失敗,會自旋等待,直到修改成功。

CAS實現的基石:Unsafe類

CAS想要保證操作時線程安全的,一個實現的關鍵在於如何保證 比較並替換 是一個原子操作

在Java中,用Unsafe類來實現CAS的原子操作,Unsafe類 ==> JNI(Java本地介面) ==>本地實現的C++庫 ==>操作記憶體空間

CAS在Java中的應用和缺點

應用:

  • Atomic包, Lock包下系列的類

  • 在JDK1.6以後,sychronized升級為重量級鎖之前也採用的CAS機制

缺點

  • CAS採用自旋的方式,會浪費CPU的資源

  • 不能保證代碼塊的原子性,保證的是對一個變數的 比較和替換 的操作是原子的

  • ABA問題:CAS操作記憶體值,由A改成了B,但是又改回了A,從而導致後續本不應該成功的操作,最後成功執行

​ 自己舉個慄子:由於網路延時,線程一線程二都想對記憶體值A操作,目的就是將記憶體值A改成B(只修改一次),按理說一個線程操作成功,那另一個線程就要操作失敗。線程一和線程二取到的舊的值都是A,假定線程一操作成功,將A改成了B。按理說接下來線程二拿到的記憶體的值是B,和取到的舊的值A比較,B不等於A,就會提交失敗,但是捏,好巧不巧,線上程二修改之前,線程三過來執行它自己的任務,將B改成了A。這個時候線程二拿到的記憶體值是A,之前取到的舊的值也是A,A等於A,線程二就會對A進行修改。這個時候就對記憶體值修改了兩次,而我們只想讓它修改一次,就出錯了。可以把記憶體值想成自己的工資,誰都不想自己的工資被莫名其妙的多改幾次把,改多了當我沒說,哈哈哈。

ABA的解決方案:給數據加上版本號,每次不僅要比較記憶體的值,還要比較版本號

4. AQS

AQS是什麼?

AQS(AbstractQueuedSynchronizer,抽象隊列同步器)是構建鎖和其他同步組件的基礎框架

AQS能幹什麼?

  • 同步隊列的管理和維護
  • 同步狀態的光臨
  • 線程的阻塞,喚醒的管理

基本設計思路

  • 把競爭的線程和等待狀態,封裝成為Node對象
  • AQS把這些Node,放到一個同步隊列中去,這個同步隊列是一個FIFO(先進先出)的一個雙向隊列,是基於CLH(貢獻者名字縮寫首字母,不用糾結這個)隊列實現的

  • AQS使用int類型的成員變數來表示同步狀態,比如:是否有線程獲取鎖,鎖的重入次數等,具體的含義由具體的子類來定義
  • AQS使用LockSupport來實現對線程的喚醒和阻塞,線程的喚醒和阻塞便隨著同步隊列的維護。

AQS如何把基礎功能提供出去?

AQS使用模板方法模式,大概的意思是規定了整體的流程,自己可以具體實現子流程,整體的流程是不能變的。後續把設計模式學了再做補充

非阻塞的獲取獨占鎖的流程

自己畫的簡化版流程,沒有涉及到裡面的中斷

AQS中獲取和釋放獨占鎖和共用鎖區別

獨占鎖:正常情況下,只有持有鎖的線程運行結束了,釋放鎖了,該節點才會出隊。

共用鎖:當前節點喚醒了下一個節點並且將下一個節點設置尾Head之後,該節點出隊。

獨占鎖:只有在釋放鎖的時候,才會去看看要不要喚醒下一個節點。

共用鎖:在獲取鎖的過程中會在兩個地方看看要不要去喚醒下一個節點。一個是在獲取鎖的流程中調用setHeadAndPropagate()方法的時候,一個是在釋放鎖的時候。

5. ReentrantLock

ReentrantLock是Lock介面的實現,主要實現了可重入的獨占鎖的功能,與synchronized關鍵字功能類型

ReentrantLock與synchronized對比

ReentrantLock功能更加強大和靈活

  • 可非中斷的獲取鎖
  • 可中斷式的獲取鎖
  • 可超時獲取鎖
  • 提供了公平鎖和非公平鎖

公平鎖和非公平鎖的卻別主要體現在獲取鎖的方式上

公平鎖:多個線程按照申請獲取鎖的先後順序來獲取鎖

非公平鎖:多個線程按照不是按照申請獲取鎖的先後順序來獲取鎖。比如搶占式獲取鎖。高併發的情況可能會造成饑餓現象

在ReentrantLock的源碼中,公平鎖主要是通過判斷當前的AQS隊列是否有節點來控制當前節點是否獲得鎖。隊列中如果有節點那麼tryAcquire()方法直接返回false表示獲取鎖失敗,再將節點其排到隊列末尾。

ReentrantReadWriteLock

在實際的業務中,往往讀數據比寫數據更加頻繁,如果我們對讀數據使用共用鎖,對寫數據使用獨占鎖,那麼整個讀寫的性能就會提高。

讀鎖:用在讀取臨界資源的地方

寫鎖:用在更新臨界資源的地方

讀鎖和寫鎖的互斥規則:

  • 一個線程,另一個線程:共用
  • 一個線程,另一個線程:互斥
  • 一個線程,另一個線程:互斥
  • 一個線程,另一個線程:互斥

ReadWriteLock是一個介面,該介面中只有兩個方法,分別為Lock readLock();Lock writeLock();

ReentrantReadWriteLock:可重入式讀寫鎖,是讀寫鎖(ReadWriteLock)的實現類。

  • 支持讀鎖和寫鎖
  • 支持公平鎖和非公平鎖
  • 支持可重入鎖
  • 支持鎖降級(如果一個線程持有寫鎖,在不釋放寫鎖的情況下,它還可以繼續持有讀鎖,這種情況就是鎖降級)

讀寫鎖的狀態存儲機制

AQS里的state是一個int值。在讀寫鎖中,需要同時保存兩種鎖的狀態。其同樣使用int類型的變數表示state,總共32位,前16位表示讀鎖的同步狀態,後面16位表示寫鎖的同步狀態。獲取讀鎖狀態就將state無符號右移16位。獲取寫鎖狀態就將state與掩碼相與,保留後16位。

6. StampedLock類

ReentrantReadWriteLock中存在著一些問題,寫線程可能會出現“饑餓”問題;如果有線程在讀,那麼寫線程是無法獲取寫鎖的。

優點:

在Java8中引入了StampedLock,其對ReentrantReadWriteLock進行了增強,優化了讀鎖和寫鎖的訪問,使讀寫鎖之間可以相互轉換,因此可以更細粒度地控制併發。

缺點:

其設計初衷使作為一個內部工具類來使用,用於輔助開發其他的線程安全組件。用不好的花會產生死鎖,產生莫名其妙的問題。不支持可重入也是一個問題。

特點

  • 所有獲取鎖的方法,都會返回一個stamp
  • 所有釋放鎖的方法,都需要一個stamp
  • 是不可重入的
  • 有三種訪問方式,分別為讀模式,寫模式,樂觀讀模式
  • 支持讀鎖和寫鎖的相互轉換
  • 不支持Condition

7. Condition

該介面對原生的wait, notify/notifyAll這些方法進行增強,從Java語言層面,實現類似的功能。

AQS是使用同步隊列來控制節點獲取鎖,在Condition中使用條件隊列來控制節點什麼時候await(),什麼時候signal()。與多個節點共用一個同步隊列不同的是,一個Conditon對象就對應一個條件隊列。

總體流程為,調用await()時,將節點加入等待隊列,然後將線程掛起,等待其他線程對其調用signal()方法。其他線程對其調用signal()方法後,將該節點從條件隊列中出隊,將其添加到同步隊列的末尾,然後將其喚醒。然後就走同步隊列的那一套流程。

8. ThreadLocal

ThreadLocal是用來存放線程自身相關數據的一個容器。提供線程本地變數,訪問這個變數的每個線程都會有這個變數的一個副本。線程操作數據的時候就會操作線程本地的數據,從而避免了線程安全性問題。

threadLocals其實是一個ThreadLocalMap類型的,在Thread類中的一個屬性,伴隨的線程的存在而存在。當我們設置ThreadLocal變數的時候,ThreadLocalMap中的key就是ThreadLocal,value就是ThreadLocal變數的值。

  • 由於threadLocals是Thread的一個屬性,會跟著線程一直存在,為了避免記憶體溢出,在確定ThreadLocal數據以後不再使用後,要及時remove掉。

  • 由於ThreadLocalMap使用ThreadLocal的弱引用作為key,如果一個ThreadLocal沒有外部關聯的強引用,在垃圾回收的時候,JVM會回收掉ThreadLocal,就會出現ThreadLocalMap中key為空,但是value值還在。造成記憶體泄漏,所以在確定ThreadLocal數據以後不再使用後,要及時remove掉。


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

-Advertisement-
Play Games
更多相關文章
  • 前言 當前的主瀏覽器都支持直接打開pdf文件,從而實現文件預覽。如果是其他格式文件則得下載,因此用openOffice實現文件轉pdf格式。 一、 openOffice的安裝 下載地址:http://www.openoffice.org/ 安裝教程可參考:openOffice下載和安裝 進入安裝目錄 ...
  • 本文只是通過一個實例來講述如何獲得python中所有的單字元的字母表,不僅僅是局限於英文的abcd,可能還有其他語言如ᵝᵞᵟᵠ等。在實際寫python的過程中可能不一定用得到,但是不失為一個挺有趣的功能探索。 ...
  • 在C/C++中有個叫指針的玩意存在感極其強烈,而說到指針又不得不提到記憶體管理。現在時不時能聽到一些朋友說指針很難,實際上說的是記憶體操作和管理方面的難。(這篇筆記咱也會結合自己的理解簡述一些相關的記憶體知識) 最近在寫C程式使用指針的時候遇到了幾個讓我印象深刻的地方,這裡記錄一下,以便今後回顧。 “經一 ...
  • 面向對象和麵向過程的區別 區別簡述 面向過程(Procedure Oriented):以過程為核心,強調**事件的流程、順序,**如:C語言。 面向對象(Object Oriented):以對象為核心,強調**事件的角色、主體,**如:C++、Java。 區別 1.思路不同 2.特點不同 3.優勢不 ...
  • 現象: 最近將pyinsatller升級到最新的 Version: 5.0.1版本後(之前一直用的是3.5版本同樣方法未遇到問題,今次更新到最新版本後5.0.1後打包就遇到問題,具體是這中間哪個版本開始有變化也不清楚了,也不去追究,凡是在新版本中遇到問題就在新版本中解決),詳細現象及解決辦法如下: ...
  • 昨天突發奇想想來玩一玩,然後安裝了一下午才成功,基本所有該踩的坑都踩了,但當時沒截圖,現在靠著記憶寫一下。 官網鏈接:https://www.mongodb.com/try/download/community。 需要註意的是:超過次數就必須要登錄才能下載。(證明我真的試過很多次) 1.最開始出現的 ...
  • 今天某個項目的數據有些問題,需要查詢日誌看看具體的情況 結果在執行 cat ***.log |grep "關鍵字" 命令後包如下錯誤: grep: memory exhausted 思路1: 既然提示 記憶體問題,是不是日誌文件太大了, 用 du -sh * 命令查看 後文件也就 300M 思路2: ...
  • 下載&安裝Cmake 進入下載頁面 Download | CMake 選擇安裝包版本 打開安裝包,下一步之後選擇添加path 選擇完安裝文件夾開始安裝 下載&配置OpenCV 進入下載頁面 Releases - OpenCV 選擇版本下載(我下的是 Sources,Windows版是已經構建好的,不 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...