2013-06-04 156 views
3

我有一個場景,我想使用StringBuilder作爲方法中的局部變量。我明白如果StringBuilder是一個本地方法變量,它不應該有任何有關線程安全的問題。Java:使用StringBuilder進行線程安全

但是,如果我追加到StringBuilder一個實例變量,如:

class MyClass { 
    private List<String> property; 

    public void myMethod() { 
     StringBuilder sb = new StringBuilder(); 
     for(String s : property) { 
      sb.append(s); 
     } 
    } 

    // some other methods that mutate property 
} 

我想使這個線程安全的,簡單地改變StringBuilderStringBuffer是不夠的。我應該在財產本身上進行同步嗎?

+0

我的方法在做什麼?似乎它正在創建一個StringBuilder對象並將它扔掉...... – stivlo

+1

您需要同步訪問'List',而不是'StringBuilder' –

+0

您可能想要在屬性之間進行分隔。 –

回答

7

這不是StringBuilder而是List<String> property誰處於危險之中。你有兩個選擇:

  1. myMethod和變異財產​​

  2. 使用java.util.concurrent.CopyOnWriteArrayList這是線程安全的,並就迭代快照等方法

+1

只是OP的FYI:如果你計劃進行大量更新,警惕'CopyOnWriteArrayList'方法;在這種情況下,您最好選擇1。 –

2

看來,同步問題在於財產。

所以只需添加一個同步就可以了:

synchronized (property) { 
    ... 
} 
3

您可以在property同步,或任何物體上,只要你在任何地方訪問property相同對象上同步。

同步另一種方法是使用一個線程安全的實現的List,如CopyOnWriteArrayList,這是偉大的(快,爭不到同步於)若閱讀列表往往但是不能修改過於頻繁。

4

問題是,當你打電話給你的人myMethod(),另一個線程可以添加新的String到你的property列表,從而修改結果。

避免這種情況的一種方法是在以任何方式訪問屬性的方法中使用​​,或者您可以使用舊的ReadWriteLock。最常用的實現是ReentrantReadWriteLock。在互聯網上有很好的例子,但你會做的是這樣的:

class MyClass { 
    private final ReadWriteLock propertiesLock = new ReentrantReadWriteLock(); 
    private final Lock read = propertiesLock.readLock(); 
    private final Lock write = propertiesLock.writeLock(); 
    ... 

    public StringBuilder myMethod() { 
     StringBuilder builder = new StringBuilder(); 
     read.lock(); 
     try { 
      // your writing here. 
     } finally { 
      read.unlock(); 
     } 
     return builder; 
    } 

    public void addProperty(String property) { 
     write.lock(); 
     try { 
      properties.add(property); 
     } finally { 
      write.unlock(); 
     } 
    } 
}