并发、阻塞队列 (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);
}

“并发包有很多优点。我们只需要非常了解这些类就可以使用它们。”

“我明白了。谢谢,金。这些课程真的很有趣。我希望有一天我能像大师一样掌握它们。”