我的Previous question让我想到了这个问题。
ArrayList线程的add function是否安全?
我使用以下类创建了一个示例应用程序
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 {
}
即使我无法复制previous question上的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)
Form ArrayList Code这是抛出错误的行:
if (index > size || index < 0)
throw new IndexOutOfBoundsException(
"Index: "+index+", Size: "+size);
但是我们可以从Exception中看到 index为1且size为10 ,因此如果条件满足则无法实现。那么我的假设是正确的,即arrayList的add函数是不安全的还是其他的东西?
答案 0 :(得分:4)
(这个类大致相当于Vector,除了它是不同步的。)
您需要自己实现同步,或者更好的是,使用像Vector这样的同步容器。
对于代码,您有2个线程运行相同的代码段(loadObject
),其中访问/修改了几个静态值。您需要确保每个访问都以同步方式完成。
你有两个线程,因此你分配了ThreadTest.list
字段的两倍,所以其中一个分配是没用的,但更重要的是,在它丢失之前可能会在该列表中插入一些值,因此这些值也会丢失
您应确保在分配列表之前未分配列表。
您也可能遇到isLoaded字段的问题,导致列表中包含10个以上的元素。
答案 1 :(得分:4)
简短的回答,不,这不是线程安全的。来自JavaDoc
请注意,此实现未同步。如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素的任何操作,或显式调整后备数组的大小;仅设置元素的值不是结构修改。)这通常通过同步一些自然封装的对象来实现。名单。如果不存在此类对象,则应使用Collections.synchronizedList方法“包装”该列表。这最好在创建时完成,以防止意外地不同步访问列表:
List list = Collections.synchronizedList(new ArrayList(...));
通常,Java中的所有现代集合都不是线程安全的。如果您只是想让它们不被炸毁,您可以使用JavaDoc中建议的Collections.synchronizedList
。但值得注意的是,这只意味着一次只有一个线程可以访问该集合。这确实使其安全,但可能导致线程被阻止的问题。
如果你想获得高并发性,那么你真的想看看java.util.concurrent
package。这为你提供了很好的类ArrayBlockingQueue
,这使得这种线程非常容易。更好的是,看看Executor
实现可以为您处理很多这种复杂性。
答案 2 :(得分: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;
,已更新,在多线程环境中,它可能会导致严重的问题。