2010-03-26 43 views
9

我有一些關於Java的assigment的問題。Java分配問題 - 這是原子嗎?

  • 字符串

我有一個類:

public class Test { 
private String s; 

public synchronized void setS(String str){ 
    s = s + " - " + str; 
} 

public String getS(){ 
    return s; 
} 
} 

在我的二傳手我使用「同步」,並避免它在我消氣,因爲在我的應用程序,有大量的數據獲取和很少的設置。必須同步設置以避免不一致。我的問題是:正在獲取和設置一個可變的原子?我的意思是,在多線程環境中,Thread1即將設置變量s,而Thread2即將獲取「s」。有什麼方法可以讓getter方法獲得與s的舊值或s的新值不同的東西(假設我們只有兩個線程)? 在我的應用程序中,獲取新值並不是問題,並且獲得舊值不是問題。但我能得到別的東西嗎?

  • 那麼HashMap的獲取和放置呢?

考慮這個:

public class Test { 
     private Map<Integer, String> map = Collections.synchronizedMap(new HashMap<Integer, String>()); 

     public synchronized void setMapElement(Integer key, String value){ 
     map.put(key, value); 
     } 

     public String getValue(Integer key){ 
     return map.get(key); 
     } 
} 

是理順和得到原子? HashMap如何處理一個元素?它是否首先刪除舊的價值,並把現在的價值?除了舊價值或新價值以外,我能獲得嗎?

在此先感謝!

+0

+1用於指定讀者不需要查看絕對最新值。當然,這應該在生產代碼中記錄下來。 – 2010-03-26 18:24:32

回答

6

在第一種情況下,String恰巧對不安全發佈是安全的(在「新」Java存儲模型(JMM)中),所以這沒關係。

不是volatile從理論上講,沒有最新值的問題存在一些問題,但最新的含義尚不清楚。你可以用一個比較換盤(CAS)循環來替換鎖,但是這個鎖可能不會被爭奪,這可能不會給你帶來太多的性能收益。

HashMap的情況下,如果有另一個線程寫入它,即使是單個寫入器線程,則非同步映射也不安全。事實上,這被發現導致運行流行軟件的生產系統出現無限循環。問題中的代碼實際上爲地圖使用了兩個鎖,它位於頂部(儘管如果使用迭代器,您需要顯式保持相同的鎖)。不是final會阻止包含的類對不安全的發佈是安全的。如果mapvolatile,並且您爲每個put創建了一個新映射,那麼可以在get不同步的情況下使其安全。

+0

我正在使用Collections.synchronizedMap。它也危險嗎?我只在我的制定者中進行結構修改。所以我同步這些方法,但我的getters只是獲取數據。我是否應該同步它們?我擔心,當我在HashMap中設置一個值時,另一個線程想要獲取值,並且它會獲得比舊值或新值更多的值。 – Bob 2010-03-26 19:03:19

+0

'Collections.synchronizedMap'增加了同步,顧名思義,所以你不必這樣做。如果您將代碼嵌入到您的代碼中並直接使用'HashMap',您可能會明白它的作用。 – 2010-03-26 22:01:16

6

而不是將你的HashMap包裝在某些東西中使其同步,請考慮使用java.util.concurrency.ConcurrentHashMap

這是HashMap的更新版本,它保證「檢索反映了最近完成的更新操作的結果,它們在發生時持有」。

3

早期的回答是正確的,指出新的(1.5+)JVMs,字符串版本是安全的,關於數據損壞。你似乎意識到不同步的非同步訪問;不一定通過獲取者可以看到更改。

但是:更有用的問題是:是否有理由在此同步?如果這只是爲了有興趣知道這一點,那很好。 但是對於實際的代碼,讀寫的一般規則是,如果可以同時存在,則兩者都應該同步。因此,雖然在這種情況下你可以省略同步(可能意味着線程看不到其他線程所做的更改),但這樣做似乎沒有什麼好處。

+0

我的應用程序是一個類似於應用程序的「博客引擎」。我將新創建的entrys的第一個X(比方說10)存儲在一個變量中。用戶沒有獲得最新的entrys版本並不是問題,因爲他們可以刷新頁面,如果他們想的話。我在一天(二傳手)製作了20個主播,但我每天都有數十萬的訪問者。我認爲同步getter方法效率不高(我有很多像上面這樣的getter方法)。 (不,我每天還沒有成千上萬的訪問者,但我希望我的應用能夠處理該問題) – Bob 2010-03-26 18:49:01

+5

請注意,這不僅僅是用戶刷新頁面的問題。在Java內存模型中,如果setter線程和getter線程之間沒有同步,則getter線程可能永遠不會獲取更新後的值。 這聽起來像對我來說是一個過早的優化類型的東西。首先得到它正確。如果您後來看到同步實際上限制了您的吞吐量,那麼您可以查看更好的方式來處理它。 「 – 2010-03-26 19:11:28

+0

」有可能getter線程永遠不會獲得更新值。「怎麼來的?我只是不明白。 「35秒300毫秒」我得到的價值,這是舊的。 「35秒500毫秒」我設定的值,比「50秒xxx毫秒」我試圖得到新的?爲什麼我可能不會獲得新的價值? – Bob 2010-03-26 19:33:29

1

在多線程環境中,您需要同步getter以確保客戶端看到s的最新值。

+0

使變量「volatile」將會同樣有效,並且不需要同步開銷 – 2010-03-26 18:25:09

+2

'volatile'具有內置的自己的同步開銷。 – Bombe 2010-03-26 20:14:39

2

也許Read Write Lock可以解決您的問題?

看看它的文檔:

甲讀寫鎖允許併發的在訪問比由一互斥鎖定允許共享數據更大的級別。它利用了這樣一個事實,即一次只有一個線程(一個編寫器線程)可以修改共享數據,在很多情況下,任何數量的線程都可以同時讀取數據(因此讀取器線程)。理論上,使用讀寫鎖定所允許的併發性增加將導致性能提高超過使用互斥鎖。 ...

+0

+1,但如果同步集合尚未使用它,我會感到驚訝。 – HRJ 2011-09-21 16:14:18