![](/img/trans.png)
[英]Synchronization between different threads/processes in Java or Android?
[英]JAVA threads (different stacks) synchronization
我有一個關於由多個線程執行的代碼同步的問題:
據我所知,每個線程都有自己的堆棧,因此,非靜態變量存在於每個線程的內存中的不同位置(對於X線程,存在包含所有非靜態變量的X堆棧)。 那么為什么需要同步任何東西呢?
我的意思是,如果線程執行的代碼包含一些類變量v1,那么每個線程都有自己的v1實例(不同的內存地址),沒有其他線程可以“觸摸”它......不是嗎?
非靜態變量存在於每個線程的內存中的不同位置
這不是真的,所以答案是
如果線程執行的代碼包含一些類變量v1,那么每個線程都有自己的v1實例(不同的內存地址),沒有其他線程可以“觸摸”它...是不是這樣
沒有。 線程可以觸及由其他線程分配和修改的對象實例,並且程序員負擔過重以確保這不會影響程序的正確性。
類成員變量存在於內存每類實例的單個位置,而不是每個線程。 確實,在內存屏障 (認為synchronized
的start {
和end }
)之間,線程可能具有對象狀態的緩存,但這與強制每線程存儲的語言不同。 “每個線程的內存”是它的堆棧,它不包含對象成員* - 只引用對象。
想到它的最好方法是堆上有一個位置,每個對象,但可能有多個讀取和寫入涉及同時發生的內存位置。
如果您聽說線程在堆的不同部分分配對象,我可以看到您將如何得出您所做的結論。 某些JVM具有優化功能,可以進行線程局部分配,但不會阻止其他線程訪問這些對象。
線程局部分配
如果分配器是真正實現的,如清單1所示,共享heapStart字段將很快成為一個重要的並發瓶頸,因為每個分配都涉及獲取保護該字段的鎖。 為了避免這個問題,大多數JVM使用線程局部分配塊,其中每個線程從堆中分配更大的內存塊,並按順序從該線程局部塊中提供小的分配請求。 因此,線程必須獲取共享堆鎖的次數大大減少,從而提高了並發性。
堆棧是線程安全的,而堆不是線程安全的,除非您同步代碼。 堆棧包含局部變量和方法參數(原始和引用),而堆包含對象。
只保證在堆棧上分配原始類型,例如int
。 對象和數組通常都存儲在堆中,除非Escape Analysis確定對象的范圍 “ 僅限於過程的范圍 ”。
堆棧是(想想一個調用堆棧,局部變量),但類變量存在於堆中,你必須同步訪問它們:)
在同一個對象實例上 ,如果您的方法未同步,則無法保證相同的代碼不會在不同的線程中執行兩次 - > havoc! 哪個是正確的值?
至少,您希望將訪問變量的方法聲明為synchronized。 如果您想要更精細的控件,可以使用ReentrantReadWriteLock
。
聲明一個方法同步在對象實例上同步,所以這是安全的。
局部變量,基元和引用是隱式線程局部的。 但是,引用的對象可以共享,當線程可以修改共享對象時,很可能需要synchronised
,鎖定或其他策略來確保線程安全。
一些關鍵點可以幫助澄清您的疑慮 -
對象始終在堆上分配。
類級別變量在線程之間共享 (同一對象的線程)
局部變量始終是線程安全的(如果不以非線程安全的方式暴露給外部世界)
“存在於不同位置的非靜態變量”可能不正確。 在Java中,您永遠不會直接了解“堆棧”的任何內容。 所有類變量static或instance都來自堆。 但是,作為一名Java開發人員,您並不真正關心它。
你不關心線程安全的唯一時間是你的類是不可變的(在構造之后不要改變)或者你沒有在線程中做任何事情。 如果您的課程不屬於這兩個類別,您需要考慮使它們具有線程安全性。
您可以在設計中獲得的不變性越多,線程問題就越容易推理和克服。
Nrj有正確的想法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.