Java ArrayList.add(int索引,E元素)线程是否不安全?
我的朋友把我带到这个问题上来 ArrayList的线程安全吗 我用下面的类制作了一个示例应用程序Java ArrayList.add(int索引,E元素)线程是否不安全?,java,multithreading,arraylist,thread-safety,Java,Multithreading,Arraylist,Thread Safety,我的朋友把我带到这个问题上来 ArrayList的线程安全吗 我用下面的类制作了一个示例应用程序 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 m
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;
}
}
即使我无法复制我在计算机上遇到的Null指针异常
,我有时也会遇到这个错误
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);
if(索引>大小| |索引<0)
抛出新的IndexOutOfBoundsException(
索引:“+Index+”,大小:“+Size”);
但正如我们从异常中看到的,索引是1,大小是10,所以如果条件满足,则不可能。那么,我的假设正确吗,arrayList的add函数是线程不安全的,还是这里发生了其他事情?来自:
(该类大致相当于Vector,只是不同步。)
您需要自己实现同步,或者更好地使用一个同步容器,如
对于代码,有两个线程运行同一段代码(loadObject
),其中访问/修改了多个静态值。您需要确保以同步方式完成每个访问。
您有2个线程,因此您分配了两次ThreadTest.list
字段,因此其中一个分配是无用的,但更重要的是,在该列表丢失之前可能会插入一些值,因此这些值也会丢失
在分配列表之前,应确保未分配该列表
isLoaded字段也可能有问题,导致列表中的元素超过10个。简短回答,不,它不是线程安全的。从 请注意,此实现是不同步的。如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改该列表,则必须在外部对其进行同步。(结构修改是添加或删除一个或多个元素,或显式调整支持数组大小的任何操作;仅设置元素的值不是结构修改。)这通常通过在自然封装列表的某个对象上进行同步来完成。如果不存在此类对象,则应使用Collections.synchronizedList方法“包装”列表。最好在创建时执行此操作,以防止意外不同步地访问列表:
List list = Collections.synchronizedList(new ArrayList(...));
一般来说,Java中的现代集合都不是线程安全的。如果您只是想让它们不会爆炸,您可以使用JavaDoc中建议的Collections.synchronizedList
。但是值得注意的是,这只意味着一次只有一个线程可以访问集合。这确实使其安全,但可能会导致线程被阻塞的问题
如果您试图获得高并发性,那么您真的想看看。这将为您提供很好的类,如,这使得这种线程的传递非常容易。更好的是,看看可以为您处理大量这种复杂性的实现。我已经放置了
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;
,它会被更新,在多线程环境中,它可能会导致严重的问题。还要注意:同步类的每个方法并不一定会使整个代码线程安全。示例:使用
indexOf()
后跟set(int,Object)
替换找到的对象,如果在两次调用之间修改列表(即使是在完全同步的列表上),可能会产生意外的结果。感谢您的回答,那么这应该是意外的原因behaviour@Ankur:您上一个问题中的问题肯定是由于一些未正确同步的并行工作造成的。不同步的列表可能是最小的问题。例如,我认为构建和使用这样的(类似静态的)列表应该是单独的步骤,并且您似乎同时运行这两个步骤,这是一个坏主意。只需在代码上喷洒synchronize
或调用collections.synchronizedList()
,将无法解决根本问题(但可能会隐藏一些症状)。@Ankur:代码的位置无关紧要。如果它同时运行,那么您必须仔细研究如何同步它。解决此问题的最安全(可能也是最简单)的方法是:确保除生成线程之外的任何线程都不能访问列表
,直到它被完全构造。@JoachimSauer Thank将确保只有一个线程将对象添加到arrayListarrayList
作为一个整体不是线程安全的,因此,该方法可能也不是线程安全的。当并发运行时,不能期望不正确的同步代码工作。Arraylist不是线程安全的,并且您不将访问同步到布尔标志。即使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++;
}
private transient Object[] elementData;