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

What will be the output accessing a list on different threads?

我有一个 ArrayListString 并且两个线程正在同时访问该列表。 以下代码段的输出结果是什么?为什么?

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.

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

在 java 中,迭代器本质上是快速失败的,因此一旦它们意识到下划线结构已被更改,它们就会失败并出现 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");
        }
    }

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

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

Synchronized Keyword
Its overall purpose is to only allow one thread at a time into a particular section of code thus allowing us to protect, for example, variables or data from being corrupted by simultaneous modifications from different threads.

所以这应该适合你:

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();

}

注意: 这取决于你想要什么样的逻辑。 您是要在遍历列表时将其从列表中删除还是?您希望这两个任务按顺序发生吗?

synchronizedList 的 Javadoc 明确指出:

It is imperative that the user manually synchronize on the returned list when iterating over it:

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

Failure to follow this advice may result in non-deterministic behavior.

因为在这段代码中,您在迭代它时从 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();