ArrayList.add(int index,E element)线程不安全吗?

我之前的问题让我想到了这个问题。

添加 ArrayList线程的function安全吗?

我用以下类创建了一个示例应用程序

import java.util.ArrayList; import java.util.List; public class ThreadTest { public static List 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(); 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(); } } } } 

这是我的假class

 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索引中看到的那样索引是1而大小是10 ,所以如果满足条件就没有办法。 所以我的假设是正确的,arrayList的add函数是线程不安全还是其他的东西在这里?

从文档 :

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

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

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

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

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

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

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

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

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

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

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

 /** * 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; 

,这是更新的,在multithreading环境中,它可以导致至关重要的问题。