2011-03-09 59 views
5

假設我有一個方法,它可以被兩個或多個線程訪問,並且我想讓它成爲線程安全的。同步java中的形式參數

public int getVal(int x, int y, MyClass myObj) 
{ 
    int z; 

    z = getInt(myObj); 

    return x + y + z; 
} 

在這裏,我認爲我們不必爲x + y同步,因爲它們是基元。

我們假設getInt(myObj)修改myObj的狀態並影響z的值。

因此,我將不得不爲行z = getInt(myObj);提供同步,但只有當兩個線程都在'myObj'引用中傳遞相同的實例時。作爲API的編碼器,我不知道兩個線程是否會爲'myObj'傳遞相同的實例。在某些情況下,這些線程可能會在'myObj'引用中傳遞相同的MyClass實例,而在其他情況下,它們可能會在'myObj'引用中傳遞不同的MyClass實例。

那麼,如何確保行z = getInt(myObj)的線程安全? (當然,我們不希望在傳遞的實例不同時進行同步,並且只有在傳遞的實例相同時才需要進行同步,我很清楚這無法確定)。

假設MyClass不能做不可變的,我認爲以下可能是一個解決方案。

synchronized(myObj) 
{ 
    z = getInt(myObj); 
} 

這是一個正確的解決方案嗎?而且,在其他什麼方式才能保證線程安全的

z = getInt(myObj); (but only in case of different instances)? 
+0

您的同步塊已足夠。每當同步對象的監視器被另一個線程佔用時,JVM就會阻止該調用。 – 2011-03-09 19:25:38

+0

@Brent,有沒有其他方法可以做到這一點?就是想! – 2011-03-09 19:33:12

+0

您的synchronized()是危險的。在下面檢查我的答案。 – iluxa 2011-03-09 20:11:42

回答

0

要回答「並在哪些方面我們可以保證線程安全的......但只有在不同情況下的情況下」,同步整個方法或創建另一個公共對象,以充當所有線程的鎖並在其上同步而不是myObj。

3

你有什麼是正確的。當你在一個對象上創建它時,它的鎖定不在那個類上。因此,如果我將相同的 *實例*的對象傳遞給兩個不同的方法,它將正確鎖定該對象。不過,如果我通過兩個不同的實例,將不會有任何鎖定,因爲兩個實例各自擁有

+3

更好的方法是同步MyClass上的getInt方法。 – 2011-03-09 19:36:28

+0

他有什麼是正確的,但不好的做法。通過鎖定myObj,他可能會向客戶端代碼引入陷阱,並希望在該對象上進行同步。 http://stackoverflow.com/questions/442564/avoid-synchronizedthis-in-java/442601#442601 – 2011-03-09 20:49:22

+0

是的,但如果他委託工作getInt()並使用內部對象來做鎖定,我認爲他會組。但是,我同意這有一個陷阱。 – 2011-03-09 20:52:41

2

如果getInt不修改this的狀態,則該方法是線程安全的。 myObj對象的線程安全性是其類的響應性:MyClass或持有它的對象。恕我直言,不是所有可能將其作爲論點的方法的責任。

但是,您的解決方案(synchronized(myObj))是正確的:如果在兩個線程中都使用相同的myObj,則兩個線程將不能同時執行getInt方法。如果兩個myObjs不同,它們將同時執行。

+0

如果某人同時調用'setInt'會怎麼樣?如果別人可能寫作,你仍然可能會遇到問題。 – 2011-03-09 20:06:54

+0

這不是一個getter(訪問器),我會假定它從輸入中提取或派生某些東西,所以它不太可能存在setInt。 – Robin 2011-03-09 21:57:57

+0

「myObj對象的線程安全性是其類的責任」:Java庫的本質是線程安全常見嗎?或者,像集合庫一樣,是否假設用戶將在適當的位置添加鎖定? – Karmastan 2011-03-10 02:45:05

0

如果有問題的myObject唯一的變化來自這getInt方法,那麼你的同步就足夠了。如果還有其他修改器,請確保它們在同一個對象上同步。

0

我不同意所有「你的同步是正確的」答案。如果你的用戶有2個線程,並且其中一個線程已經在對象上持有一個鎖,該怎麼辦?僵局隨之而來。

另外,x + y + z不是原子的。在CPU的水平,這將成爲

int temp = x + y; 
int res = temp + z; 

,我會告訴你更多:long1 + long2是不能在32位機器原子。

我認爲你唯一的選擇是同步整個方法。

+1

在他的方法中添加基元很好,因爲基元是通過值傳遞的。一個方法的原始形式參數不能在線程間共享,因爲它們是按值傳遞的。 – 2011-03-09 20:37:28

1
synchronized(myObj) { z = getInt(myObj); } 

會做你想要的,但在參數表上同步會產生許多其他問題。例如,某個其他線程可能已經在該對象上同步(例如,可能該對象具有正在對其調用的同步方法),並且可能陷入死鎖情況。

同步應該像其他任何東西一樣被封裝。最好的解決方案是將getInt方法添加到MyClass,並在該方法中的某個私有成員上進行同步。通過這種方式,沒有其他人可以利用你正在使用的來實現你的syngrhonization。

例如爲:

public class MyClass { 
    private Object lockObject = new Object(); 
    public int getInt() { 
    synchronized(lockObject) { 
     // do your thing 
    } 
    } 
} 

見這樣的:在參考Avoid synchronized(this) in Java? 到封裝的同步的重要性。