2011-05-27 182 views
3

再次提出有關ArrayList和同步的問題。ArrayList,線程和同步 - 如何準確同步多個線程

我只是想知道這是什麼片斷究竟:

ArrayList<ObjectX> list = ....; 

synchronized (list) { 
    if (list.contains(objectxy) == false) { 
     list.add(objectxy); 
    } 
} 

Ive得到了充滿ObjectXs一個ArrayList中。我想添加一個元素到列表中,但只有當列表不包含相同的元素。我之前(在另一種方法中)檢查列表是否包含對象 - 結果是否定的。但是有可能兩個線程同時認爲結果是否定的,並且他們都嘗試添加objectxy。 (還有其他一些必須在中間做的事情,這就是爲什麼我不能同步整個過程)

所以,在過程之後,當現在的線程來到上面的片段,我想防止這兩個都添加該對象到列表中。所以我想當我同步訪問列表時,只有一個線程可以檢查它是否包含對象,然後添加它。在它之後,第二個線程可以訪問列表,查看該對象已經在其中並且不再添加它。

那就是我想要達到的。 它會工作嗎? :-)

所以,如果是的話,我想知道片段到底是什麼。是否阻止兩個線程同時訪問此確切的代碼?這樣代碼只能同時用於一個線程

或者它鎖定列表本身整個時間,在應用程序的任何線程是誰在這個時候嘗試訪問列表 - 隨時隨地? (我沒有其他的add()在我的代碼中,但許多gets(),這就是爲什麼Id喜歡知​​道其他線程是否可以訪問列表,並仍然獲取元素,而另一個線程訪問上面的代碼)。

ArrayList本身是一個使用應用程序與主體連接的成員變量。多個不同的線程同時訪問上面的代碼是正確的如果它們不是從相同的主體發送的,那麼正確的?

所以,這就是Id想知道的。我試圖標記我的問題,以便更容易地回答它們。感謝您的幫助! :-)

[編輯]謝謝你所有的答案,這幾乎所有的說法都一樣!我認爲現在很清楚! :-)

  • 同步代碼塊只能由其中一個主體線程訪問。 (其他校長的線索與特殊校長無關)。該列表本身可以隨時從其他線程訪問 - 只要對它的訪問不與同步塊同步即可。如果是這樣,線程必須等待,直到它可以訪問列表(這意味着沒有其他線程同時在同步塊中)

是否正確?我希望如此:-)

回答

3

同步塊保證只有一個線程可以執行該代碼塊,或者同時在同一對象(即列表)上同步的任何其他代碼塊。例如,如果你有

synchronized (list) { 
    // block A 
} 

synchronized (list) { 
    // block B 
} 

,那麼如果一個線程正在執行塊A,沒有其他的線程可以被執行嵌段A或嵌段B,因爲它們都在同一對象上同步。但列表本身並未鎖定。另一個線程可能訪問列表。

5

你幾乎擁有它。​​可防止鎖定同一list對象的其他線程同時運行其代碼塊。它不會鎖定list對象本身。其他線程仍然可以訪問它,如果他們不同步在同一個對象上。

2

是的,一次只有一個線程可以訪問該代碼塊。所有其他線程將等待,直到首先到達的線程將完成代碼塊的執行。

另外,您對用戶主體的假設是正確的。如果您的數組列表是每個用戶(主體)的數組,則只有與該用戶(主體)一起執行的線程才需要在該特定數組列表上同步。

1

除了同意像其他答案一樣建議它只會阻塞同一塊代碼。 我認爲你有的關鍵困惑,並且大多數人都有關於鎖定在同步(鎖定)。你使用列表本身作爲你的情況下的鎖。但是,使用一個對象作爲鎖,並且如果對象中的代碼將被阻止,則完全不相關。實際上,只要它是同一個對象,就可以使用任何對象作爲鎖。這意味着如果你有一個名爲另一個成員變量,下面的代碼將運行基本相同:

synchronized (foo) { 
    if (list.contains(objectxy) == false) { 
     list.add(objectxy); 
    } 
} 

每個對象都可以被用作一個鎖。

0

只要所有線程在做任何事情之前在對象上同步,它就會工作。常見做法是隱藏其他人的列表並給予只讀副本。

public class ThreadSafeList { 

    private ArrayList<String> list = new ArrayList<String>(); 


    public synchronized void addUnique(String s) { 
     if (!list.contains(s)) { 
      list.add(s); 
     } 
    } 


    public synchronized List<String> getList() { 
     return Collections.unmodifiableList((new ArrayList<String>(list))); 
    } 
} 

通過封裝來保證同步。

同步方法類似於

public void addUnique(String s) 
    synchronized(this){ 
    list.add(s); 
    } 

和Java有你sychronize沒有什麼區別,但它是安全有單獨的鎖定對象。

0

您也可以使用Vector(同步列表)或其他同步集合。 如果您正在進行多次讀取但寫入較少,則可以使用CopyOnWriteArrayList

但是,如果你經常在寫作,它會產生很多開銷。

+0

感謝您的回答。我不使用synchronizedList,因爲我對它有非常少的操作需要同步 - 這就是爲什麼我認爲這是沒有必要的。由於CopyOnWriteArrayList也是線程安全的,我認爲這對我的應用程序來說也太多了 - 我實際上並不需要在每個點都進行同步。 :-) – nano7 2011-05-27 10:16:56

+0

這種方法要非常小心:您必須確保*每個*列表訪問都不會與任何其他訪問重疊。例如,如果有一個線程執行get()而另一個執行同步(list){add()/ set()}指令的機會,您必須*同步get()指令。 – Flavio 2011-05-27 10:27:18

+0

爲什麼?如果僅僅因爲用戶可以獲得不是最新的數據(可能缺少一個添加),那麼這並不重要 - 至少不適用於我的應用程序,我的意思是:-)我可以接受那個 – nano7 2011-05-27 12:01:38