簡體   English   中英

Java線程 - 同步問題

[英]Java Thread - Synchronization issue

來自Sun的教程:

同步方法啟用了一種簡單的策略來防止線程干擾和內存一致性錯誤:如果一個對象對多個線程可見,則對該對象變量的所有讀取或寫入都是通過同步方法完成的。 (一個重要的例外:構造對象后無法修改的最終字段,一旦構造了對象,就可以通過非同步方法安全地讀取)這種策略是有效的,但是可能會帶來活性問題,因為我們將會請參閱本課后面的內容。

Q1。 上述語句是否意味着如果一個類的對象將在多個線程之間共享,那么該類的所有實例方法 (除了最終字段的getter)都應該同步,因為實例方法處理實例變量?

為了理解Java中的並發性,我推薦了非常有價值的Java Concurrency in Practice

在回答您的具體問題時,盡管同步所有方法是實現線程安全的快捷方式,但它根本無法很好地擴展。 考慮備受詬病的Vector類。 每個方法都是同步的,並且它的工作非常繁瑣,因為迭代仍然不是線程安全的。

不是。這意味着同步方法是實現線程安全的一種方法,但它們不是唯一的方法,並且它們本身並不能保證在所有情況下都完全安全。

不必要。 例如,您可以同步(例如,鎖定專用對象)訪問對象變量的方法的一部分。 在其他情況下,您可以將作業委派給已經處理同步問題的某些內部對象。
有很多選擇,這取決於您正在實施的算法。 雖然'synchronized'關鍵字通常是最簡單的。

編輯
沒有全面的教程,每種情況都是獨一無二的。 學習它就像學習一門外語:永遠不會結束:)

但肯定有用的資源。 特別是,Heinz Kabutz的網站上有一系列有趣的文章。
http://www.javaspecialists.eu/archive/Issue152.html (參見頁面上的完整列表)

如果其他人有任何鏈接,我也有興趣看到。 我發現整個主題非常混亂(可能是核心java中最困難的部分),特別是因為java 5中引入了新的並發機制。

玩得開心!

最一般的形式是。

不可變對象不需要同步。

此外,您可以為可變實例變量(或其中的組)使用單獨的監視器/鎖定,這將有助於生動。 同時只同步數據更改的部分,而不是整個方法。

synchronized methodName vs synchronized(object)

這是正確的,是另一種選擇。 我認為同步訪問該對象只會同步其所有方法會更有效。

雖然差異可能很微小,但如果您在單個線程中使用同一個對象,那將非常有用

即(在方法上使用synchronized關鍵字)

class SomeClass {
    private int clickCount  = 0;

    public synchronized void click(){
        clickCount++;
    }
 }

當像這樣定義一個類時,一次只有一個線程可以調用click方法。

如果在單線程應用程序中過於頻繁地調用此方法會發生什么? 您將花費一些額外的時間來檢查該線程是否可以在不需要時獲取對象鎖定。

class Main {
    public static void main( String  [] args ) {
         SomeClass someObject = new SomeClass();
         for( int i = 0 ; i < Integer.MAX_VALUE ; i++ ) {
             someObject.click();
         }
    }
 }

在這種情況下,檢查線程是否可以鎖定對象將被不必要地調用Integer.MAX_VALUE (2 147 483 647)次。

因此,在這種情況下刪除synchronized關鍵字將運行得更快。

那么,您將如何在多線程應用程序中執行此操作?

您只需同步對象:

synchronized ( someObject ) {
    someObject.click();
}

向量與ArrayList

另外請注意,這種用法( syncrhonized methodName與syncrhonized(object) )順便說一下, java.util.Vector現在被java.util.ArrayList替換的原因之一。 許多Vector方法都是同步的。

大多數情況下,列表用於單線程應用程序或代碼段(即jsp / servlet中的代碼在單個線程中執行),Vector的額外同步對性能沒有幫助。

HashtableHashMap取代也是如此

事實上,getter a也應該同步,或者字段要變得volatile 那是因為當你獲得一些價值時,你可能會對這個價值的最新版本感興趣。 你看, synchronized塊語義不僅提供了執行的原子性(例如,它保證了一次只有一個線程執行這個塊),而且還提供了可見性。 這意味着當線程進入synchronized塊時,它會使其本地緩存無效,當它熄滅時,它會將已修改的所有變量轉儲回主內存。 volatile變量具有相同的可見性語義。

不可以。即使是吸氣劑也必須同步,除非他們只訪問最終字段。 原因是,例如,當訪問一個long值時,另一個線程當前正在寫入一個微小的變化,並且只讀了前4個字節而其他4個字節仍然是舊值時讀取它。

對,那是正確的。 修改數據或訪問可能由不同線程修改的數據的所有方法都需要在同一監視器上同步。

簡單的方法是將方法標記為同步。 如果這些是長時間運行的方法,您可能只想同步讀/寫的那些部分。 在這種情況下,您將定義監視器,以及wait()和notify()。

簡單的答案是肯定的。 如果該類的對象將由多個線程共享,則需要同步getter和setter以防止數據不一致。 如果所有線程都具有單獨的對象副本,則不需要同步方法。 如果您的實例方法不僅僅是設置和獲取,您必須分析等待長時間運行的getter / setter完成的線程的威脅。

您可以使用同步方法,同步塊,並發工具(如Semaphore或者如果您真的想要使用,可以使用Atomic References。 其他選項包括將成員變量聲明為volatile並使用類AtomicInteger而不是Integer

這一切都取決於具體情況,但有很多可用的並發工具 - 這些只是其中的一部分。

同步可能導致hold-wait死鎖,其中兩個線程各自擁有一個對象的鎖,並試圖獲取另一個線程對象的鎖。

同步對於類來說也必須是全局的,並且容易犯的錯誤是忘記同步方法。 當一個線程持有一個對象的鎖時,其他線程仍然可以訪問該對象的非同步方法。

暫無
暫無

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

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