java對象鎖,java對象鎖和類鎖

本文目錄一覽:

在 Java 程序中怎麼保證多線程的運行安全?

2.1.讀一致性

Java 中針對上述「讀不安全」的問題提供了關鍵字 volatile 來解決問題,被 volatile 修飾的成員變數,在內容發生更改的時候,會通知所有線程去主內存更新最新的值,這樣就解決了讀不安全的問題,實現了讀一致性。

但是,讀一致性是無法解決寫一致性的,雖然能夠使得每個線程都能及時獲取到最新的值,但是1.1中的寫一致性問題還是會存在。

既然如此,Java 為啥還要提供 volatile 關鍵字呢?這並非多餘的存在,在某些場景下只需要讀一致性的話,這個關鍵字就能夠滿足需求而且性能相對還不錯,因為其他的能夠保證「讀寫」都一直的辦法,多多少少存在一些犧牲。

2.2.寫一致性

Java 提供了三種方式來保證讀寫一致性,分別是互斥鎖、自旋鎖、線程隔離。

2.2.1.互斥鎖

互斥鎖只是一個鎖概念,在其他場景也叫做獨佔鎖、悲觀鎖等,其實就是一個意思。它是指線程之間是互斥的,某一個線程獲取了某個資源的鎖,那麼其他線程就只能睡眠等待。

在 Java 中互斥鎖的實現一般叫做同步線程鎖,關鍵字 synchronized,它鎖住的範圍是它修飾的作用域,鎖住的對象是:當前對象(對象鎖)或類的全部對象(類鎖)——鎖釋放前,其他線程必將阻塞,保證鎖住範圍內的操作是原子性的,而且讀取的數據不存在一致性問題。

對象鎖:當它修飾方法、代碼塊時,將會鎖住當前對象

類鎖:修飾類、靜態方法時,則是鎖住類的所有對象

注意: 鎖住的永遠是對象,鎖住的範圍永遠是 synchronized 關鍵字後面的花括弧劃定的代碼域。

2.2.2.自旋鎖

自旋鎖也只是一個鎖概念,在其他場景也叫做樂觀鎖等。

自旋鎖本質上是不加鎖,而是通過對比舊數據來決定是否更新:

java多線程,對象鎖是什麼概念?

java線程:

1.線程中一些基本術語和概念

1.1線程的幾個狀態

初始化狀態

就緒狀態

運行狀態

阻塞狀態

終止狀態

1.2 Daemon線程

Daemon線程區別一般線程之處是:主程序一旦結束,Daemon線程就會結束。

1.3鎖的定義

為了協調多個並發運行的線程使用共享資源才引入了鎖的概念。

1.4死鎖

任何多線程應用程序都有死鎖風險。當一組線程中的每一個都在等待一個只

有該組中另一個線程才能引起的事件時,我們就說這組線程死鎖了。換一個說法

就是一組線程中的每一個成員都在等待別的成員佔有的資源時候,就可以說這組

線程進入了死鎖。死鎖的最簡單情形是:線程 A 持有對象 X 的獨佔鎖,並且

在等待對象 Y 的鎖,而線程 B 持有對象 Y 的獨佔鎖,卻在等待對象 X 的鎖。

除非有某種方法來打破對鎖的等待(Java 鎖定不支持這種方法),否則死鎖的線

程將永遠等下去。

1.5.Java對象關於鎖的幾個方法

1.5.1 wait方法

wait方法是java根對象Object含有的方法,表示等待獲取某個鎖。在wait方法進入前,會釋放相應的鎖,在wait方法返回時,會再次獲得某個鎖。

如果wait()方法不帶有參數,那只有當持有該對象鎖的其他線程調用了notify或者notifyAll方法,才有可能再次獲得該對象的鎖。

如果wait()方法帶有參數,比如:wait(10),那當持有該對象鎖的其他線程調用了notify或者notifyAll方法,或者指定時間已經過去了,才有可能再次獲得該對象的鎖。

參考 thread.lock.SleepAndWait

1.5.2 notify/notifyAll方法

這裡我就不再說明了。哈哈,偷點懶。

1.5.3 yield方法

yield()會自動放棄CPU,有時比sleep更能提升性能。

1.6鎖對象(實例方法的鎖)

在同步代碼塊中使用鎖的時候,擔當鎖的對象可以是這個代碼所在對象本身或者一個單獨的對象擔任,但是一定要確保鎖對象不能為空。如果對一個null對象加鎖,會產生異常的。原則上不要選擇一個可能在鎖的作用域中會改變值的實例變數作為鎖對象。

鎖對象,一種是對象自己擔任,一種是定義一個普通的對象作為private property來擔任,另外一種是建立一個新的類,然後用該類的實例來擔任。

參考 :

thread.lock.UseSelfAsLock,使用對象自己做鎖對象

thread.lock.UseObjAsLock 使用一個實例對象作鎖對象

thread.lock.UseAFinalObjAsLock使用常量對象作為一個鎖對象

1.7類鎖

實例方法存在同步的問題,同樣,類方法也存在需要同步的情形。一般類方法的類鎖是一個static object來擔任的。當然也可以採用類本身的類對象來作為類鎖。

一個類的實例方法可以獲得該類實例鎖,還可以嘗試去訪問類方法,包含類同步方法,去獲得類鎖。

一個類的類方法,可以嘗試獲得類鎖,但是不可以嘗試直接獲得實例鎖。需要先生成一個實例,然後在申請獲得這個實例的實例鎖。

參考

thread.lock.UseStaticObjAsStaticLock 使用類的屬性對象作為類鎖。

thread.lock.UseClassAsStaticLock使用類的類對象作為類鎖

1.8.線程安全方法與線程不安全方法

如果一個對象的所有的public方法都是同步方法,也就是說是public方法是線程安全的,那該對象的private方法,在不考慮繼承的情況下,可以設置為不是線程安全的方法。

參考 thread.lock.SynMethrodAndNotSynMethrod

1.9類鎖和實例鎖混合使用

在實例方法中混合使用類鎖和實例鎖;可以根據前面說的那樣使用實例鎖和類鎖。

在類方法中混合使用類鎖和實例鎖,可以根據前面說的那樣使用類鎖,為了使用實例鎖,先得生成一個實例,然後實例鎖。

參考 thread.lock.StaticLockAndObjLock

1.10鎖的粒度問題。

為了解決對象鎖的粒度過粗,會導死鎖出現的可能性加大,鎖的粒度過細,會程序開發維護的工作加大。對於鎖的粒度大小,這完全要根據實際開發需要來考慮,很難有一個統一的標準。

1.11.讀寫鎖

一個讀寫鎖支持多個線程同時訪問一個對象,但是在同一時刻只有一個線程可以修改此對象,並且在訪問進行時不能修改。

有2種調度策略,一種是讀鎖優先,另外就是寫鎖優先。

參考 thread.lock.ReadWriteLock

1.12 volatile

在Java中設置變數值的操作,除了long和double類型的變數外都是原子操作,也就是說,對於變數值的簡單讀寫操作沒有必要進行同步。這在JVM 1.2之前,Java的內存模型實現總是從主存讀取變數,是不需要進行特別的注意的。而隨著JVM的成熟和優化,現在在多線程環境下volatile關鍵字的使用變得非常重要。在當前的Java內存模型下,線程可以把變數保存在本地內存(比如機器的寄存器)中,而不是直接在主存中進行讀寫。這就可能造成一個線程在主存中修改了一個變數的值,而另外一個線程還繼續使用它在寄存器中的變數值的拷貝,造成數據的不一致。要解決這個問題,只需要像在本程序中的這樣,把該變數聲明為volatile(不穩定的)即可,這就指示JVM,這個變數是不穩定的,每次使用它都到主存中進行讀取。一般說來,多任務環境下各任務間共享的標誌都應該加volatile修飾。

2.線程之間的通訊

在其他語言中,線程之間可以通過消息隊列,共享內存,管道等方式來實現

線程之間的通訊,但是java中可以不採用這樣方式,關注的是線程之間的同步。

只要保證相關方法運行的線程安全,信息共享是自然就可以顯現了。

2.1屏障

屏障就是這樣的一個等待點: 一組線程在這一點被同步,這些線程合併各自的結果或者運行到整體任務的下一階段。

參考:

thread.lock. BarrierUseExample

thread.lock.Barrier

2.2.鎖工具類

提供對線程鎖的獲取,釋放功能。展示了鎖的獲取釋放過程。可以作為一個工具類來使用。

參考:thread.lock. BusyFlag

2.3.條件變數

條件變數是POSIX線程模型提供的一種同步類型,和java中的等待通知機制類似。

雖然java中已經有了等待通知機制,但是為了減少在notify/notifyAll方法中

線程調度的開銷,把一些不需要激活的線程屏蔽出去,引入了條件變數。

Java中2個(多個)條件變數可以是同一個互斥體(鎖對象)。

參考:thread.lock.CondVar 條件變數類

常見的應用情形:

一個鎖控制多個信號通道(例如:多個變數),雖然可以採用簡單java等待通知機制,但是線程調度效率不高,而且線程可讀性也不是太好,這時候可以採用創建一個鎖對象(BusyFlag實例),同時使用這個BusyFlag實例來創建多個條件變數(CondVar 實例)。

經常使用到CondVar類的地方是緩衝區管理,比如:管道操作之類的。先創建一個BusyFlag實例,然後創建CondVar 實例,用這個條件變數描述緩衝區是否為空,另外創建CondVar 實例作條件變數述緩衝區是否滿。

現實中,馬路的紅綠燈,就可以採用條件變數來描述。

3. Java線程調度

3.1 Java優先順序

java的優先順序別共有10種,加上虛擬機自己使用的優先順序別=0這種,總共11種。

大多數情況來說,java線程的優先順序設置越高(最高=10),那線程越優先運行。

3.2. 綠色線程

線程運行在虛擬機內,操作系統根本不知道這類線程的存在。

線程是由虛擬機調度的。

3.3 本地線程

線程是由運行虛擬機的操作系統完成的。

3.4 Windows本地線程

操作系統,完全能夠看得到虛擬機內的每一個線程,同時虛擬機的線程和操作系統的線程是一一對應的。Java的線程調度室由操作系統底層線程決定的。

在win32平台下,windows線程只有6個優先順序別。和java線程優先順序別對應如下:

Java線程優先順序 Windows 95/nt/2000線程優先順序

0 THREAD_ PRIORITY_IDLE

1(Thread.MIN_PRIORITY) THREAD_ PRIORITY_LOWEST

2 THREAD_ PRIORITY_LOWEST

3 THREAD_ PRIORITY_BELOW_NORMAL

4 THREAD_ PRIORITY_BELOW_NORMAL

5 (Thread.NORM_PRIORITY) THREAD_ PRIORITY _NORMAL

6 THREAD_ PRIORITY _ABOVE_NORMAL

7 THREAD_ PRIORITY _ABOVE_NORMA

8 THREAD_ PRIORITY _HIGHEST

9 THREAD_ PRIORITY _HIGHEST

10 (Thread.MAX_PRIORITY) THREAD_ PRIORITY _CRITICAL

3.5線程優先順序倒置與繼承

如果一個線程持有鎖(假設該線程名字=ThreadA,優先順序別=5),另外一個線程(假設該線程名字=ThreadB,優先順序別=7),現在該線程(ThreadA)處於運行狀態,但是線程ThreadB申請需要持有ThreadA所獲得的鎖,這時候,為了避免死鎖,線程A提高其運行的優先順序別(提高到ThreadB的優先順序別=7),而線程ThreadB為了等待獲得鎖,降低線程優先順序別(降低到ThreadA原來的優先順序別=5).

上述的這種情況,對於ThreadA,繼承了ThreadB的優先順序別,這成為優先順序別的繼承;對於ThreadB暫時降低了優先順序別,成為優先順序別的倒置。

當然,一旦線程ThreadA持有的鎖釋放了,其優先順序別也會回到原來的優先順序別(優先順序別=5)。線程ThreadB獲得了相應的鎖,那優先順序別也會恢復到與原來的值(優先順序別=7)。

3.6循環調度

具有同樣優先順序的線程相互搶佔成為循環調度。

4.線程池

創建一個線程也是需要一定代價的,為了降低這個代價,採用了和普通對象池的思想建立線程池,以供系統使用。

線程消耗包括內存和其它系統資源在內的大量資源。除了 Thread 對象所需的內存之外,每個線程都需要兩個可能很大的執行調用堆棧。除此以外,JVM 可能會為每個 Java 線程創建一個本機線程,這些本機線程將消耗額外的系統資源。最後,雖然線程之間切換的調度開銷很小,但如果有很多線程,環境切換也可能嚴重地影響程序的性能。

使用線程池的方式是,先建立對象池,然後申請使用線程,程序線程運行,運行完畢,把線程返回線程池。

使用線程池的風險:同步錯誤和死鎖,與池有關的死鎖、資源不足和線程泄漏。

大家有空可以研究一下tomcat的線程池實現原理思想。

實際上是tomcat已經在從線程池的使用線程時候加上了事件處理機制。

個人認為,線程池之類的實現,一般不要自己實現,因為自己實現主要是穩定性等方面可能作的不夠好。

可以參考 apache的jakarta-tomcat-5.5.6的相關代碼,具體是:

jakarta-tomcat-connectors\util\java\org\apache\tomcat\util\threads的相關代碼

5工作隊列

使用工作隊列的好處是不象直接使用線程池那樣,當線城池中沒有線程可以使用的時

候,使用者需要處於等待狀態,不能進行其他任務的處理。

工作隊列的工作原理是:

採用後台線程處理方式,客戶端把任務提交給工作隊列,工作隊列有一組內部可以工作線程,這些工作線程從工作隊列中取出任務運行,一個任務完成後,就從隊列獲取下一個任務進行處理。當工作隊列中沒有任務可以處理時候,工作線程就處於等待狀態,直到獲得新的任務時候,才進行新的處理。

java同步代碼塊的對象鎖是什麼?

對象鎖你是可以自己指定的,你可以把當前類對象傳過來,那代碼塊的對象鎖就是這個類對象。在線程間通信的時候,notify只能喚醒擁有相同鎖的線程。

java 對象鎖和方法鎖有什麼區別

對象鎖類鎖

對象鎖

當一個對象中有synchronized

method或synchronized

block的時候調用此對象的同步方法或進入其同步區域時,就必須先獲得對象鎖。如果此對象的對象鎖已被其他調用者佔用,則需要等待此鎖被釋放

同步靜態方法/靜態變數互斥體

由於一個class不論被實例化多少次,其中的靜態方法和靜態變數在內存中都只由一份。所以,一旦一個靜態的方法被申明為synchronized。此類所有的實例化對象在調用此方法,共用同一把鎖,我們稱之為類鎖。一旦一個靜態變數被作為synchronized

block的mutex。進入此同步區域時,都要先獲得此靜態變數的對象鎖

類鎖

由上述同步靜態方法引申出一個概念,那就是類鎖。其實系統中並不存在什麼類鎖。當一個同步靜態方法被調用時,系統獲取的其實就是代表該類的類對象的對象鎖

在程序中獲取類鎖

可以嘗試用以下方式獲取類鎖

synchronized

(xxx.class)

{…}

synchronized

(Class.forName(“xxx”))

{…}

同時獲取2類鎖

同時獲取類鎖和對象鎖是允許的,並不會產生任何問題,但使用類鎖時一定要注意,一旦產生類鎖的嵌套獲取的話,就會產生死鎖,因為每個class在內存中都只能生成一個Class實例對象。

java為什麼要對對象加鎖

對象是一個鎖標誌。按照先到先得的原則,如果有多個線程都會執行代碼,並使用同一個對象作為鎖,

synchronize(對象){

….

那麼,先執行這段代碼的那個線程,將會獲得這個對象鎖,而當這個線程執行這段代碼的時候,其他線程也是使用這個對象作為鎖的,就不能執行這段代碼,知道最初得到這個鎖的線程運行完這段代碼,然後再把鎖分配給下一個線程執行。

java有沒有一種單向鎖?當執行B的時候鎖住,無法執行A

按正常的線程同步方式處理,不過在A獲得鎖後馬上釋放掉就行了。這樣B被A影響的機率非常小,為保險可以在A釋放掉鎖後通知所有被鎖對象就行了。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/159519.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-11-20 00:14
下一篇 2024-11-20 00:14

相關推薦

  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • java client.getacsresponse 編譯報錯解決方法

    java client.getacsresponse 編譯報錯是Java編程過程中常見的錯誤,常見的原因是代碼的語法錯誤、類庫依賴問題和編譯環境的配置問題。下面將從多個方面進行分析…

    編程 2025-04-29
  • Java騰訊雲音視頻對接

    本文旨在從多個方面詳細闡述Java騰訊雲音視頻對接,提供完整的代碼示例。 一、騰訊雲音視頻介紹 騰訊雲音視頻服務(Cloud Tencent Real-Time Communica…

    編程 2025-04-29
  • Java Bean載入過程

    Java Bean載入過程涉及到類載入器、反射機制和Java虛擬機的執行過程。在本文中,將從這三個方面詳細闡述Java Bean載入的過程。 一、類載入器 類載入器是Java虛擬機…

    編程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介紹

    本文將詳細介紹Java Milvus SearchParam withoutFields的相關知識和用法。 一、什麼是Java Milvus SearchParam without…

    編程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java語言中的一個版本,於2014年3月18日發布。本文將從多個方面對Java 8中某一周的周一進行詳細的闡述。 一、數組處理 Java 8新特性之一是Stream…

    編程 2025-04-29
  • Java判斷字元串是否存在多個

    本文將從以下幾個方面詳細闡述如何使用Java判斷一個字元串中是否存在多個指定字元: 一、字元串遍歷 字元串是Java編程中非常重要的一種數據類型。要判斷字元串中是否存在多個指定字元…

    編程 2025-04-29
  • VSCode為什麼無法運行Java

    解答:VSCode無法運行Java是因為默認情況下,VSCode並沒有集成Java運行環境,需要手動添加Java運行環境或安裝相關插件才能實現Java代碼的編寫、調試和運行。 一、…

    編程 2025-04-29
  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29
  • Java 8 Group By 會影響排序嗎?

    是的,Java 8中的Group By會對排序產生影響。本文將從多個方面探討Group By對排序的影響。 一、Group By的概述 Group By是SQL中的一種常見操作,它…

    編程 2025-04-29

發表回復

登錄後才能評論