2013-07-04 65 views
1

此Java類是否線程安全或重置方法需要同步呢?如果是的話,有人可以告訴我爲什麼?Java在原子操作上同步

public class NamedCounter { 
    private int count; 
    public synchronized void increment() { count++; } 
    public synchronized int getCount() { return count; } 
    public void reset() { count = 0; } 
} 
+2

如果不知道所需的語義,就無法回答這個問題。你有什麼保證,如果有的話? –

+6

你爲什麼不使用[** AtomicInteger **](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicInteger.html)? – jlordo

回答

2

注意​​不只是約互斥,它基本上是關於操作在他們的行動的可見性方面的正確順序。因此,reset也必須同步,否則它所做的寫入可能會同時發生到其他兩種方法,並且不能保證可見。

總而言之,你的班級不像現在這樣是線程安全的,但是隻要你同步reset方法就行。

+2

我不認爲這個答案是有道理的。如果調用者未同步它,則不管它是否自己同步,都不安全。考慮一個線程同時調用'reset'和另一個調用'increment'。即使'reset'上的'synchronized'也不能保證結果。所以這不足以也不需要給予'重置'可預測的行爲。 –

+0

@DavidSchwartz什麼是同步高度取決於用例。因此,只要您不知道確切的用例,就不知道要在哪裏同步。 –

+0

@UwePlonus:的確如此。而且由於這個答案的建議既不足以讓班級變得可預測,這也是一個不好的答案。 –

0

您還需要同步添加到重置方法,然後它將被同步。但是通過這種方式,您可以通過鎖實現同步,也就是說,訪問該方法的每個線程都將鎖定NamedCounter對象實例。

但是,如果使用AtomicInteger作爲count變量,則不需要syncronize,因爲它使用CAS cpu操作來實現原子性,而無需同步。

8

不是沒有同步rest()和添加更多的方法。你會遇到你需要更多方法的情況。例如

NamedCounter counter = new NamedCounter(); 
counter.increment(); 
// at this exact time (before reaching the below line) another thread might change changed the value of counter!!!! 
if(counter.getCount() == 1) { 
    //do something....this is not thread safe since you depeneded on a value that might have been changed by another thread 
} 

要解決上面你需要像

NamedCounter counter = new NamedCounter(); 
if(counter.incrementAndGet()== 1) { //incrementAndGet() must be a synchronized method 
    //do something....now it is thread safe 
} 

相反,使用Java的儲存卡,在課堂上的AtomicInteger涵蓋所有情況。或者如果你正在嘗試學習線程安全性,那麼使用AtomicInteger作爲標準(從中學習)。

對於產品代碼,無需考慮兩次就可以與AtomicInteger一起使用!請注意,使用AtomicInteger不會自動保證代碼中的線程安全。你必須使用api提供的方法。他們在那裏是有原因的。

+0

+1推薦AtomicInteger – Dahaka

+1

請注意,在嚴重爭用下,由於過多的重試次數,CAS將執行*更糟糕的操作。與任何其他的*樂觀鎖定計劃一樣,CAS對於低到中等的爭用是完美的。 –

+0

這裏是一個閱讀[CAS vs鎖定](http://stackoverflow.com/questions/2664172/java-concurrency-cas-vs-locking) – Multithreader

1

您還必須同步reset()方法。

要使線程安全,您必須同步全部訪問變量的路徑,否則您將在非同步路徑中產生不希望的結果。

+0

你怎麼知道他想要什麼結果?你爲什麼認爲他不在乎同時調用'reset'和'increment'是否會產生不可預知的不一致結果。請參閱我對Marko的評論 - 這對於使課程可預測既不必要也不足夠。 –

0

不是一個答案,但過長的註釋:

如果reset()在synch'ed,那麼0成爲讀取或遞增計數器後任何線程可見。沒有同步,就沒有可視性保證。查看併發增量與非同步重置的交互作用,可能是0在進入方法之前對增量線程可見,則結果將爲1.如果在增量的讀取和寫入之間將計數器設置爲0,重置將被遺忘。如果在寫入後設置,最終結果將爲0.因此,如果要對每個讀取線程斷言,復位後計數器爲0,則該方法也必須同步。但是大衛施瓦茨是正確的,那些低層次的同步沒有什麼意義,而沒有更高層次的語義相互作用。