多線程筆記(二)

来源: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
  • 一個自定義WPF窗體的解決方案,借鑒了呂毅老師的WPF製作高性能的透明背景的異形視窗一文,併在此基礎上增加了滑鼠穿透的功能。可以使得透明窗體的滑鼠事件穿透到下層,在下層窗體中響應。 ...
  • 在C#中使用RabbitMQ做個簡單的發送郵件小項目 前言 好久沒有做項目了,這次做一個發送郵件的小項目。發郵件是一個比較耗時的操作,之前在我的個人博客裡面回覆評論和友鏈申請是會通過發送郵件來通知對方的,不過當時只是簡單的進行了非同步操作。 那麼這次來使用RabbitMQ去統一發送郵件,我的想法是通過 ...
  • 當你使用Edge等瀏覽器或系統軟體播放媒體時,Windows控制中心就會出現相應的媒體信息以及控制播放的功能,如圖。 SMTC (SystemMediaTransportControls) 是一個Windows App SDK (舊為UWP) 中提供的一個API,用於與系統媒體交互。接入SMTC的好 ...
  • 最近在微軟商店,官方上架了新款Win11風格的WPF版UI框架【WPF Gallery Preview 1.0.0.0】,這款應用引入了前沿的Fluent Design UI設計,為用戶帶來全新的視覺體驗。 ...
  • 1.簡單使用實例 1.1 添加log4net.dll的引用。 在NuGet程式包中搜索log4net並添加,此次我所用版本為2.0.17。如下圖: 1.2 添加配置文件 右鍵項目,添加新建項,搜索選擇應用程式配置文件,命名為log4net.config,步驟如下圖: 1.2.1 log4net.co ...
  • 之前也分享過 Swashbuckle.AspNetCore 的使用,不過版本比較老了,本次演示用的示例版本為 .net core 8.0,從安裝使用開始,到根據命名空間分組顯示,十分的有用 ...
  • 在 Visual Studio 中,至少可以創建三種不同類型的類庫: 類庫(.NET Framework) 類庫(.NET 標準) 類庫 (.NET Core) 雖然第一種是我們多年來一直在使用的,但一直感到困惑的一個主要問題是何時使用 .NET Standard 和 .NET Core 類庫類型。 ...
  • WPF的按鈕提供了Template模板,可以通過修改Template模板中的內容對按鈕的樣式進行自定義。結合資源字典,可以將自定義資源在xaml視窗、自定義控制項或者整個App當中調用 ...
  • 實現了一個支持長短按得按鈕組件,單擊可以觸發Click事件,長按可以觸發LongPressed事件,長按鬆開時觸發LongClick事件。還可以和自定義外觀相結合,實現自定義的按鈕外形。 ...
  • 一、WTM是什麼 WalkingTec.Mvvm框架(簡稱WTM)最早開發與2013年,基於Asp.net MVC3 和 最早的Entity Framework, 當初主要是為瞭解決公司內部開發效率低,代碼風格不統一的問題。2017年9月,將代碼移植到了.Net Core上,併進行了深度優化和重構, ...