如何证明arraylist对测试不是线程安全的?

时间:2013-09-24 13:43:43

标签: java arraylist thread-safety

在我们的应用程序中,我们在ArrayList.add(Object o)操作上得到了一个ArrayIndexOutOfBounds异常。最明显的解释是线程安全,但我无法重新创建事件。我试过创建两个线程。在一个我添加元素,在另一个我正在删除它们(或清除数组),但我没有第二次得到异常。 我的意思是很明显它可以通过查看ArrayList的源代码来实现,但是能够演示它会很好。

我已经运行了这个测试很长一段时间没有任何例外:

public class Test {
static ArrayList a = new ArrayList();

public static void main(String[] args) throws Exception {
    Thread t1 = new Thread() {
        public void run() {
            while (true) {
                if (a.size() > 0)
                    a.remove(0);
            }
        }
    };

    Thread t2 = new Thread() {
        public void run() {
            while (true) {
                a.add(new Object());
            }
        }
    };

    t2.start();
    Thread.sleep(100);
    t1.start();
}
}

4 个答案:

答案 0 :(得分:6)

感谢isnot2bad的评论,我在我的假设中发现了一个问题。 问题在于并发添加,而不是添加/删除。 我能够创建一个失败的测试:

static ArrayList a = new ArrayList(1);

public static void main(String[] args) throws Exception {
    Thread t1 = new Thread() {
        public void run() {
            while (true) {
                a.add(new Object());
            }
        }
    };

    Thread t2 = new Thread() {
        public void run() {
            while (true) {
                a = new ArrayList(1);
                a.add(new Object());
                a.add(new Object());
            }
        }
    };

    t2.start();
    Thread.sleep(100);
    t1.start();
}

在第一个帖子的添加行中,我得到了这个:

Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 2 

:)

答案 1 :(得分:2)

使用给定代码很难观察到任何错误,因为您实际上并未检查列表中存储的内容。我不能说得到一个ArrayIndexOutOfBoundsException是不可能的,但是这将是非常罕见的,因为你只能在调整数组大小时获得一个,并且它很少调整大小。

如果检查删除的对象不是重复的,则更有可能看到意外行为:您只添加新对象,因此删除的线程永远不会看到同一个对象两次,对吧?不是这样:

import java.util.*;
public class Test {
    static ArrayList a = new ArrayList();

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread() {
            public void run() {
                Object x = null;
                while (true) {
                    if (a.size() > 0) {
                        Object y = a.remove(0);
                        if (x == y) System.out.println("Duplicate!");
                        x = y;
                    }
                }
            }
        };

        Thread t2 = new Thread() {
            public void run() {
                while (true) {
                    a.add(new Object());
                }
            }
        };

        t2.start();
        Thread.sleep(100);
        t1.start();
    }
}

System.arrayCopy调用期间添加对象时会发生这种情况:elementData[--size] = null将错误的数组索引设置为null,因为size不再具有其在{{1}}中的值方法的开头。

答案 2 :(得分:1)

我可以通过添加更多的加法器线程来重现您的问题。

答案 3 :(得分:0)

让您的消费者线程比生产者睡眠时更少睡眠,例如20毫秒而不是100毫秒。这样,抛出异常的可能性要大得多。