4

我的上一个问题使我想到了这个问题。

ArrayList线程的add函数安全吗?

我用以下类制作了一个示例应用程序

import java.util.ArrayList;
import java.util.List;


public class ThreadTest
{
    public static List<DummyObject> list = null;
    public static boolean isLoaded = false;
   public static void main(String [] args)
   {
      MyThread t1 = new MyThread(1);
      MyThread t2 = new MyThread(2);

      t1.start();
      t2.start();
   }

   public static void loadObject(){
       if(isLoaded){
           return;
       }
       isLoaded = false;
       try{
       list = new ArrayList<DummyObject>();
       for(int i=0;i<10;i++){
           list.add(i,new DummyObject());
       }}
       catch(Exception e){
           e.printStackTrace();
       }
       isLoaded = true;
   }
}

这些是我的主题

public class MyThread extends Thread
{
   int threadNumber ;
   public MyThread(int threadNumber)
   {
      this.threadNumber = threadNumber;
   }

   @Override
   public void run()
   {
       try {
        sleep(10-threadNumber);
    } catch (InterruptedException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
     System.out.println("Running Thread: " + threadNumber);
     ThreadTest.loadObject();
     if(ThreadTest.isLoaded){
         System.out.println(ThreadTest.list);
         for(int i=0;i<ThreadTest.list.size();i++){
             if(ThreadTest.list.get(i)==null){
                 throw new NullPointerException();
             } 
         }
     }else {
         try {
                sleep(10);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
         }
   }
}

这是我的虚拟班

public class DummyObject {

}

即使我无法复制Null Pointer Exception我在上一个问题中得到的内容,我有时也会收到此错误

Exception in thread "Thread-1" java.lang.IndexOutOfBoundsException: Index: 1, Size: 10
    at java.util.ArrayList.add(ArrayList.java:367)
    at ThreadTest.loadObject(ThreadTest.java:25)
    at MyThread.run(MyThread.java:20)

表单 ArrayList 代码这是引发错误的行:

if (index > size || index < 0)
        throw new IndexOutOfBoundsException(
        "Index: "+index+", Size: "+size);

但正如我们从 Exception index 为 1 和 size 为 10中看到的那样,因此无法满足 if 条件。那么我的假设是否正确,即 arrayList 的 add 函数是线程不安全的,还是这里发生了其他事情?

4

3 回答 3

4

文档中:

(这个类大致相当于 Vector,只是它是不同步的。)

您需要自己实现同步,或者更好的是,使用像Vector这样的同步容器。

对于您的代码,您有 2 个线程运行同一段代码 ( loadObject),其中访问/修改了几个静态值。您需要确保每次访问都以同步方式完成。您有 2 个线程,因此您分配了两次该ThreadTest.list字段,因此其中一个分配是无用的,但更重要的是,在该列表丢失之前可能会在该列表中插入一些值,因此这些值也会丢失。

您应该确保在分配列表之前未分配列表。

您也可能对 isLoaded 字段有问题,导致列表中有超过 10 个元素。

于 2012-11-16T09:13:52.787 回答
4

简短的回答,不,它不是线程安全的。来自JavaDoc

请注意,此实现不同步。如果多个线程同时访问一个 ArrayList 实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。(结构修改是添加或删除一个或多个元素,或显式调整后备数组大小的任何操作;仅设置元素的值不是结构修改。)这通常通过同步一些自然封装的对象来完成列表。如果不存在这样的对象,则应使用 Collections.synchronizedList 方法“包装”该列表。这最好在创建时完成,以防止对列表的意外不同步访问:

List list = Collections.synchronizedList(new ArrayList(...));

一般来说,Java 中的现代集合都不是线程安全的。如果您只是想让它们不会爆炸,您可以Collections.synchronizedList按照 JavaDoc 中的建议使用。然而值得注意的是,这仅仅意味着一次只有一个线程可以访问该集合。这确实使它安全,但可能导致线程被阻塞的问题。

如果你想获得高并发,那么你真的想看看java.util.concurrentpackage。这为您提供了很好的类,例如,ArrayBlockingQueue这使得这种线程切换非常容易。更好的是,看看Executor可以为您处理很多这种复杂性的实现。

于 2012-11-16T09:17:43.073 回答
0

我已经放置了类add方法的实际代码ArrayList

/**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

这里这个方法声明它不是线程安全的,elementData因为

 private transient Object[] elementData;

,这是更新的,在多线程环境中它可能会导致至关重要的问题。

于 2012-11-16T09:23:22.807 回答