5

在 Collection 框架中,为什么外部同步比内部同步(Vector、HashTable 等)快?即使他们都使用相同的机制?

内部和外部同步的确切含义是什么?它们之间有何不同?

如果有人可以举例说明,那真的很有帮助。

4

2 回答 2

10

内部和外部同步的确切含义是什么?它们之间有何不同?

外部同步是调用者(您)使用synchronized关键字或其他锁来防止另一个类被多个线程访问。如果所讨论的类本身同步,则通常使用它——SimpleDateFormat这是一个很好的例子。如果您需要在线程之间发出信号,也可以使用它——即使在处理并发集合时也是如此。

为什么外部同步比内部同步更快(Vector、HashTable 等)?即使他们都使用相同的机制?

外部同步不一定更快。通常,一个类可以精确地确定何时需要围绕代码的关键部分进行同步,而不是调用者将所有方法调用包装在一个synchronized块中。

如果您谈论的是使用VectorandHashTable而是使用Collections.synchronizedList(...)orsynchronizedMap(...)方法的一般建议,那么这是因为VectorandHashTable被视为旧/过时的类。包裹ArrayList或被HashMap视为更好的解决方案。

有时正如@Chris 指出的那样,当您需要一个接一个地对一个类进行大量更改时,外部同步会更快。通过在外部锁定一次然后对类执行多次更改,这比在内部锁定每个更改更好。单个锁比连续调用多个锁要快。

如果有人可以举例说明,那真的很有帮助。

而不是Vector,人们通常推荐 WrappedArrayList具有更好的性能。这将非同步ArrayList类包装在外部同步它的包装类中。

List<Foo> list = Collections.synchronizedList(new ArrayList<Foo>());

一般来说,就内部与外部而言,请考虑以下类,您希望允许多个线程同时使用它:

public class Foo {
    private int count;
    public void addToCount() {
        count++;
        log.info("count increased to " + count);
    }
}

您可以使用外部同步并将每个调用包装addToCount()在一个synchronized块中:

synchronized (foo) {
   foo.addToCount();
}

或者类本身可以使用内部同步并为您进行锁定。这执行得更好,因为 logger 类不必是锁的一部分:

public void addToCount() {
    int val;
    synchronized (this) {
       val = ++count;
    }
    // this log call should not be synchronized since it does IO
    log.info("count increased to " + val);
}

当然,在这种情况下,Foo该类确实应该使用 anAtomicInteger并在内部处理它自己的重入:

private final AtomicInteger count = new AtomicInteger(0);
public void addToCount() {
    int val = count.incrementAndGet()
    log.info("count increased to " + val);
}
于 2013-09-27T16:15:07.117 回答
8

假设你在一家银行工作。每次您需要使用保险箱时,都需要将其解锁,然后在使用完毕后重新上锁。

现在假设您需要将 50 个盒子放入保险箱。你有两个选择:

  1. 单独搬运每个盒子,每次打开和关闭(非常重的)门
  2. 锁上银行的前门并保持金库打开,在不接触内部金库门的情况下进行 50 次旅行

哪个更快?(第一个选项是内部同步,第二个选项是外部同步。)

于 2013-09-27T16:18:57.003 回答