並發、阻塞隊列 (Java 7) - 1

“嗨,阿米戈!”

“嗨,金!”

“今天,我要給大家講講並發。”

Concurrency是一個 Java 類庫,其中包含針對多線程工作進行了優化的特殊類。這是一個非常有趣且內容廣泛的主題。但今天我們只是要進行介紹。該包稱為 java.util。並發包。我會告訴你幾個有趣的類。”

原子類型。

“你已經知道,即使是count++也不是線程安全的操作。當一個變量增加1時,實際上會發生三個操作。結果,當變量改變時可能會發生衝突。”

“是啊,艾莉不久前跟我說過:”

線程 1 線程 2 結果
register1 = count;
register1++;
count = register1;
register2 = count;
register2++;
count = register2;
register1 = count;
register2 = count;
register2++;
count = register2;
register1++;
count = register1;

“完全正確。然後 Java 添加了數據類型以將這些操作作為一個整體執行,即原子地執行(原子是不可分割的)。”

“例如,Java 有AtomicInteger、AtomicBoolean、AtomicDouble等。”

“假設我們需要創建一個 «counter» 類:”

例子
class Counter
{
 private int c = 0;

 public void increment()
 {
  c++;
 }

 public void decrement()
 {
  c--;
 }

 public int value()
 {
  return c;
 }
}

“你如何使這個類的對象線程安全?”

“好吧,我會讓所有方法同步並完成它:”

例子
class synchronized Counter
{
 private int c = 0;

 public synchronized void increment()
 {
  c++;
 }

 public synchronized void decrement()
 {
  c--;
 }

 public synchronized int value()
 {
  return c;
 }
}

“幹得好。但是,如果我們使用原子類型,它會是什麼樣子:”

例子
class AtomicCounter
{
 private AtomicInteger c = new AtomicInteger(0);

 public void increment()
 {
  c.incrementAndGet();
 }

 public void decrement()
 {
  c.decrementAndGet();
 }

 public int value()
 {
  return c.get();
 }
}

“你的班級和我的班級都以同樣的方式工作,但帶有 AtomicInteger 的班級工作得更快。”

“嗯,差別很小嗎?”

“是的。根據我的經驗,我總是建議先使用同步。只有當所有的應用程序代碼都寫完並且優化過程已經開始時,你才應該開始重寫代碼以使用原子類型。但無論如何,我希望你知道存在這樣的類型。即使你不主動使用它們,你也總是有機會在使用它們的地方遇到代碼。”

“我同意。這是有道理的。”

“順便說一下,你有沒有註意到原子類型不是不可變的?與標準Integer類相比,AtomicInteger包含改變其內部狀態的方法。”

“明白了。就像StringStringBuffer一樣。”

“是的,類似的東西。”

線程安全的集合。

“作為此類集合的示例,我可以介紹 ConcurrentHashMap。您如何使 HashMap 線程安全?”

“讓它的所有方法同步?”

“當然可以,但現在假設您有一個這樣的 SynchronizedHashMap,並且有數十個線程訪問它。並且每秒一百次將一個新條目添加到映射中,並且在此過程中整個對像被鎖定以進行讀寫。”

“好吧,這是標準方法。你能做什麼?”

“Java 的創造者想出了一些很酷的東西。”

“首先,他們將數據存儲在單個塊的 ConcurrentHashMap 中,但將其分成稱為“桶”的部分。當有人更改 ConcurrentHashMap 中的數據時,我們只鎖定正在訪問的桶,而不是整個對象。在其他換句話說,許多線程可以同時更改對象。”

“其次,你還記得你不能在迭代列表/映射的元素的同時更改列表嗎?這樣的代碼會拋出異常:”

不要在循環中遍歷集合的元素並同時更改它
HashMap<String, Integer> map = new HashMap<String, Integer>();

for (String key: map.keySet())
{
 if (map.get(key) == 0)
  map.remove(key);
}

“但是在 ConcurrentHashMap 中,你可以:”

不要在循環中迭代集合的元素並同時更改它”
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();

for (String key: map.keySet())
{
 if (map.get(key) == 0)
  map.remove(key);
}

“並發包有很多優點。我們只需要非常了解這些類就可以使用它們。”

“我明白了。謝謝,金。這些課程真的很有趣。我希望有一天我能像大師一樣掌握它們。”