【问题标题】:ArrayList vs Vector performance in single-threaded application单线程应用程序中的 ArrayList 与 Vector 性能
【发布时间】:2015-09-03 16:26:45
【问题描述】:

我只是在寻找为什么 ArrayList 比 Vector 快的问题的答案,我发现 ArrayList 更快,因为它不同步。 所以我的疑问是:

  1. 如果 ArrayList 不同步,为什么要在多线程环境中使用它并与 Vector 进行比较。

  2. 如果我们处于单线程环境中,那么在处理单线程时,由于没有进行同步,Vector 的性能会如何下降。

考虑到以上几点,我们为什么要比较性能?

请指导我:)

【问题讨论】:

  • 第一个answer
  • 您可以在单个线程中使用数组列表来获得“速度”提升,但是一旦您需要在线程之间传输数组列表,您就需要同步列表。当我在linkedblockingqueue中完成它时,我通常会推送一个arraylist,以便另一个线程将它放入并做它的事情,并最终通过另一个linkedlbockingqueue将它的结果发回。要记住的是,当您完成列表后,不要在该线程中再次触摸它。

标签: java vector arraylist collections


【解决方案1】:
  1. a) 在多线程程序中使用 ArrayList 的方法可以同步。

    X 类 {

    List l = new ArrayList();
    
    synchronized void add(Object e) {
        l.add(e);
    }
    ...
    

    b) 我们可以在不暴露给其他线程的情况下使用 ArrayList,此时 ArrayList 只能从局部变量中引用

    无效 x() { 列表 l = new ArrayList(); // 除了 current 没有其他线程可以访问 l ...

  2. 即使在单线程环境中,进入同步方法需要一个锁,这也是我们损失性能的地方

    public synchronized boolean add(E e) { // current thread will take a lock here
        modCount++;
    

    ...

【讨论】:

    【解决方案2】:

    如果列表不在线程之间共享,您可以在多线程环境中使用ArrayList

    如果列表在线程之间共享,您可以同步对该列表的访问。

    否则你可以使用Collections.synchronizedList()获取一个可以安全使用线程的List。

    Vector 是一个已不再使用的同步 List 的旧实现,因为内部实现基本上同步每个方法。通常,您希望同步一个操作序列。否则,您可以在迭代列表时抛出 ConcurrentModificationException 另一个线程修改它。此外,从性能的角度来看,同步每个方法都不好。

    另外在单线程环境中访问同步方法也需要执行一些操作,所以在单线程应用中Vector也不是一个好的解决方案。

    【讨论】:

      【解决方案3】:

      仅仅因为一个组件是单线程的并不意味着它不能在线程安全的上下文中使用。您的应用程序可能有自己的锁定,在这种情况下,额外的锁定是多余的工作。

      相反,仅仅因为组件是线程安全的,并不意味着您不能以不安全的方式使用它。通常,线程安全扩展到单个操作。例如。如果您使用 Iterator 并在集合上调用 next() 这是两个操作,并且在组合使用时它们不再是线程安全的。您仍然必须对 Vector 使用锁定。另一个简单的例子是

      private Vector<Integer> vec = 
      
      vec.add(1);
      int n = vec.remove(vec.size());
      assert n == 1;
      

      这至少是三个操作,但是可能出错的事情的数量比您想象的要多得多。这就是为什么你最终要自己锁定以及为什么 Vector 内部的锁定可能是多余的,甚至是不需要的。

      为了你自己的利益;

      • vec 可以随时更改另一个向量或null
      • vec.add(2) 可以发生在任何操作之间,改变大小和最后一个元素。
      • vec.remove() 可以发生在任何操作之间。
      • vec.add(null) 可以在任何操作之间发生,从而导致可能的 NullPointerException

      vec可以在这些地方/* change */

      private Vector<Integer> vec = 
      
      vec.add(1); /* change*/
      int n = vec.remove(vec.size() /* change*/);
      assert n == 1;
      

      简而言之,假设仅仅因为您使用了线程安全集合,您的代码现在是线程安全的,这是一个很大的假设。

      打破的常见模式是

      for(int n : vec) {
         // do something.
      }
      

      看起来很无害,除了

      for(Iterator iter = vec.iterator(); /* change */ vec.hasNext(); ) {
         /* change */ int n = vec.next();
      

      我用/* change */ 标记了另一个线程可以更改集合的地方,这意味着这个循环可以获得 ConcurrentModificationException(但可能不会)

      没有同步

      JVM 不知道不需要同步,所以它仍然需要做一些事情。它有一个优化来降低非竞争锁的成本,但它仍然需要工作。

      【讨论】:

        【解决方案4】:

        您需要了解基本概念才能知道上述问题的答案...

        当你说数组列表不同步而向量是同步时,我们的意思是那些类中的方法(像 add()、get()、remove() 等......)是同步的在向量类中而不是在数组列表类中。这些方法将作用于存储的数据。

        因此,保存在向量类中的数据不能被并行编辑/读取,因为添加、获取、删除方法是同步的,并且数组列表中的相同可以并行完成,因为数组列表中的这些方法不同步...

        这种并行活动使数组列表变快而向量变慢...尽管您在多线程(或)单线程环境中使用它们,但这种行为保持不变...

        希望这能回答您的问题...

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-05-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-07
          • 2023-04-07
          • 1970-01-01
          • 2018-12-25
          相关资源
          最近更新 更多