简体   繁体   English

Java 同步 Collections - 值是多少?

[英]Java Synchronized Collections - what is the value?

The following code triggers a ConcurrentModificationException very, very quickly:以下代码非常非常快地触发了 ConcurrentModificationException:

import java.util.*;
public class SynchFail {
    static List<Integer> LIST = new ArrayList<Integer>();
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    LIST.add(1);
                }
            }}).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    List<Integer> syncList = Collections.synchronizedList(LIST);
                    synchronized(syncList) {
                        for (Integer thisInt : syncList) {
                        }
                    }
                }
            }}).start();
    }
}

... whereas the following behaves as it should: ...而以下行为应如此:

import java.util.*;
public class SynchSucceed {
    static List<Integer> LIST = new ArrayList<Integer>();
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized(LIST) {
                        LIST.add(1);
                    }
                }
            }}).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized(LIST) {
                        for (Integer thisInt : LIST) {
                        }
                    }
                }
            }}).start();
    }
}

... my understanding was that synchronized collections were to prevent ConcurrentModificationException s in situations like this (but clearly they do not). ...我的理解是,同步的 collections 是为了防止在这种情况下发生ConcurrentModificationException (但显然他们没有)。

Given this: where should I make use of these?鉴于此:我应该在哪里使用这些?

In the first code snippet, you have not followed the instructions in the documentation of synchronizedList :在第一个代码片段中,您没有遵循synchronizedList 文档中的说明:

In order to guarantee serial access, it is critical that all access to the backing list is accomplished through the returned list.为了保证串行访问,对后备列表的所有访问都通过返回的列表完成是至关重要的。

In the other thread, you are adding to the list via the original LIST , not the "returned list".在另一个线程中,您通过原始LIST添加到列表中,而不是“返回列表”。 LIST is just a normal ArrayList and calling add on it won't acquire any locks or anything like that, so add could still be successfully called while the iteration is in progress. LIST只是一个普通的ArrayList并且在其上调用add不会获得任何锁或类似的东西,因此在迭代过程中仍然可以成功调用add

If you did:如果你这样做了:

final static List<Integer> LIST = Collections.synchronizedList(new ArrayList<>());
public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                LIST.add(1);
            }
        }}).start();
    new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                synchronized(LIST) {
                    for (Integer thisInt : LIST) {
                    }
                }
            }
        }}).start();
}

Then it wouldn't throw a CME.然后它不会抛出 CME。 When you call add on a synchronised list, it tries to acquire the intrinsic lock on LIST .当您在同步列表上调用add时,它会尝试获取LIST上的内在锁。 If iteration is in progress, the lock would have been already held by the other thread (since you did synchronized (LIST) {... } there), so it will wait until the iteration is over.如果迭代正在进行,则锁可能已经被另一个线程持有(因为你在那里做了synchronized (LIST) {... } ),所以它会等到迭代结束。 Compare this with the second code snippet, and notice how this saves you from writing an extra synchronized (LIST) {} block around the add call.将此与第二个代码片段进行比较,并注意这如何使您免于在add调用周围编写额外的synchronized (LIST) {}块。

Couple of things:几件事:

  1. If you need sychronized access to an ArrayList you should use Vector instead.如果您需要同步访问 ArrayList,您应该改用 Vector。 It does the same thing but its methods are syncrhonized.它做同样的事情,但它的方法是同步的。
  2. in your case, the 2nd snippet works because you are syncing over the same object LIST in both threads在您的情况下,第二个片段有效,因为您在两个线程中同步相同的 object LIST

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM