簡體   English   中英

JAVA線程(不同堆棧)同步

[英]JAVA threads (different stacks) synchronization

我有一個關於由多個線程執行的代碼同步的問題:

據我所知,每個線程都有自己的堆棧,因此,非靜態變量存在於每個線程的內存中的不同位置(對於X線程,存在包含所有非靜態變量的X堆棧)。 那么為什么需要同步任何東西呢?

我的意思是,如果線程執行的代碼包含一些類變量v1,那么每個線程都有自己的v1實例(不同的內存地址),沒有其他線程可以“觸摸”它......不是嗎?

非靜態變量存在於每個線程的內存中的不同位置

這不是真的,所以答案是

如果線程執行的代碼包含一些類變量v1,那么每個線程都有自己的v1實例(不同的內存地址),沒有其他線程可以“觸摸”它...是不是這樣

沒有。 線程可以觸及由其他線程分配和修改的對象實例,並且程序員負擔過重以確保這不會影響程序的正確性。

類成員變量存在於內存每類實例的單個位置,而不是每個線程。 確實,在內存屏障 (認為synchronized的start {和end } )之間,線程可能具有對象狀態的緩存,但這與強制每線程存儲的語言不同。 “每個線程的內存”是它的堆棧,它不包含對象成員* - 只引用對象。

想到它的最好方法是堆上有一個位置,每個對象,但可能有多個讀取和寫入涉及同時發生的內存位置。

如果您聽說線程在堆的不同部分分配對象,我可以看到您將如何得出您所做的結論。 某些JVM具有優化功能,可以進行線程局部分配,但不會阻止其他線程訪問這些對象。

線程局部分配

如果分配器是真正實現的,如清單1所示,共享heapStart字段將很快成為一個重要的並發瓶頸,因為每個分配都涉及獲取保護該字段的鎖。 為了避免這個問題,大多數JVM使用線程局部分配塊,其中每個線程從堆中分配更大的內存塊,並按順序從該線程局部塊中提供小的分配請求。 因此,線程必須獲取共享堆鎖的次數大大減少,從而提高了並發性。

* - JVM優化可能允許在堆棧分配一些對象。

堆棧是線程安全的,而堆不是線程安全的,除非您同步代碼。 堆棧包含局部變量和方法參數(原始和引用),而堆包含對象。

只保證在堆棧上分配原始類型,例如int 對象和數組通常都存儲在堆中,除非Escape Analysis確定對象的范圍僅限於過程的范圍 ”。

堆棧是(想想一個調用堆棧,局部變量),但類變量存在於堆中,你必須同步訪問它們:)

在同一個對象實例上 ,如果您的方法未同步,則無法保證相同的代碼不會在不同的線程中執行兩次 - > havoc! 哪個是正確的值?

至少,您希望將訪問變量的方法聲明為synchronized。 如果您想要更精細的控件,可以使用ReentrantReadWriteLock

聲明一個方法同步在對象實例上同步,所以這是安全的。

局部變量,基元和引用是隱式線程局部的。 但是,引用的對象可以共享,當線程可以修改共享對象時,很可能需要synchronised ,鎖定或其他策略來確保線程安全。

一些關鍵點可以幫助澄清您的疑慮 -

  1. 對象始終在堆上分配。

  2. 類級別變量在線程之間共享 (同一對象的線程)

  3. 局部變量始終是線程安全的(如果不以非線程安全的方式暴露給外部世界)

“存在於不同位置的非靜態變量”可能不正確。 在Java中,您永遠不會直接了解“堆棧”的任何內容。 所有類變量static或instance都來自堆。 但是,作為一名Java開發人員,您並不真正關心它。

你不關心線程安全的唯一時間是你的類是不可變的(在構造之后不要改變)或者你沒有在線程中做任何事情。 如果您的課程不屬於這兩個類別,您需要考慮使它們具有線程安全性。

您可以在設計中獲得的不變性越多,線程問題就越容易推理和克服。

Nrj有正確的想法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM