访问不同线程列表的输出是什么?

时间:2015-10-16 09:39:48

标签: java multithreading synchronized

我有一个ArrayList String个,两个线程同时访问列表。 以下代码段的输出结果如何?

public static void main(String[] args) {
    final ArrayList<String> list = new ArrayList<String>();

    for (int i = 0; i < 100; i++) {
        list.add("Number" + i);
    }

    new Thread() {

        public void run() {
            for (String s : list) {
                System.out.println(s);
            }
        }
    }.start();

    new Thread() {
        public void run() {
            list.remove("Number5");
        }
    }.start();

}

我尝试使用相同的代码,使用Arraylist同步Collections.synchronizedList(list)。它仍然在抛出java.util.ConcurrentModificationException

4 个答案:

答案 0 :(得分:3)

synchronizedList的Javadoc明确指出:

  

当迭代时,用户必须手动同步返回的列表:

List list = Collections.synchronizedList(new ArrayList());
 ...
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}
     

不遵循此建议可能会导致非确定性行为。

由于在此代码中,您在迭代它时从List中删除(在不同的线程中,但它是List的相同实例),ConcurrentModificationException将被抛出。

使用Collections.synchronizedList,以下代码可以正常运行。

final List<String> list = Collections.synchronizedList(new ArrayList<String>());
for (int i = 0; i < 100; i++) {
    list.add("Number" + i);
}

new Thread() {
    public void run() {
        synchronized (list) {
            for (String s : list) {
                System.out.println(s);
            }
        }
    }
}.start();

new Thread() {
    public void run() {
        synchronized (list) {
            list.remove("Number5");
        }
    }
}.start();

答案 1 :(得分:2)

您可以使用syncrhonized关键字锁定list对象。

  

同步关键字
   它的总体目的是只允许一个线程   一次进入特定的代码部分,从而允许我们   例如,保护变量或数据不被损坏   来自不同线程的同时修改。

所以这对你有用:

public static void main(String[] args) {
    ...

    new Thread() {

        public void run() {
        synchronized(list){
            for (String s : list) {
                System.out.println(s);
            }
        }
        }
    }.start();

    new Thread() {
        public void run() {
        synchronized(list){
                    list.remove("Number5");
        }
        }
    }.start();

}

NB:这取决于你想要什么逻辑btw。 您是否希望在迭代时从列表中删除或?你希望这两个任务按顺序发生吗?

答案 2 :(得分:1)

你是iterating(在第一个线程中)和updating(在第二个线程中)两个不同线程中的相同list对象,因此总有(可能性)它会抛出java.util.ConcurrentModificationException

在java Iterator中本质上是快速失败的,因此一旦他们意识到下划线结构已被更改,它们就会失败并出现ConcurrentModificationException异常。

如果要根据需要使用相同的列表对象,可以考虑使用两个线程synchronized(list)方法中的run进行同步。

    public void run() {
        synchronized(list) {
          for (String s : list) {
              System.out.println(s);
          }
        }
    }

    public void run() {
        synchronized(list) {
          list.remove("Number5");
        }
    }

答案 3 :(得分:0)

我认为您应该使用Vector而不是ArrayListThreadSafe加上使用Iterator来浏览您的列表。