java Volatile/synchronization on arraylist

我的未来我决定 提交于 2019-12-06 01:38:28

Volatile won't help you at all. The meaning of volatile is that changes made by thread A to a shared variable are visible to thread B immediately. Usually such changes may be in some cache visible only to the thread that made them, and volatile just tells the JVM not to do any caching or optimization that will result in the value update being delayed.

So it is not a means of synchronization. It's just a means of ensuring visibility of change. Moreover, it's change to the variable, not to the object referenced by that variable. That is, if you mark list as volatile, it will only make any difference if you assign a new list to list, not if you change the content of the list!


Your other suggestion was to make the ArrayList a synchronized variable. There is a misconception here. Variables can't be synchronized. The only thing that can be synchronized is code - either an entire method or a specific block inside it. You use an object as the synchronization monitor.

The monitor is the object itself (actually, it's a logical part of the object that is the monitor), not the variable. If you assign a different object to the same variable after synchronizing on the old value, then you won't have your old monitor available.

But in any case, it's not the object that's synchronized, it's code that you decided to synchronize using that object.

You can therefore use the list as the monitor for synchronizing the operations on it. But you can not have list synchronized.


Suppose you want to synchronize your operations using the list as a monitor, you should design it so that the writer thread doesn't hold the lock all the time. That is, it just grabs it for a single read-update, insert, etc., and then releases it. Grabs it again for the next operation, then releases it. If you synchronize the whole method or the whole update loop, the other thread will never be able to read it.

In the reading thread, you should probably do something like:

List<T> listCopy;

synchronized (list) {
    listCopy = new ArrayList(list);
}

// Use listCopy for displaying the value rather than list

This is because displaying is potentially slow - it may involve I/O, updating GUI etc. So to minimize the lock time, you just copy the values from the list, and then release the monitor so that the updating thread can do its work.


Other than that, there are many types of objects in the java.util.concurrent package etc. that are designed to help in situations like this, where one side is writing and the other is reading. Check the documentation - perhaps a ConcurrentLinkedDeque will work for you.

Indeed, none of the two solutions is sufficient. You actually need to synchronize the complete iteration on the arraylist, and every write access to the arraylist:

synchronized(list) {
    for (T t : list) {
        ...
    }
}

and

synchronized(list) {
    // read/add/modify the list
}
  1. make the ArrayList volatile.

You can't make an ArrayList volatile. You can't make any object volatile. The only things in Java that can be volatile are fields.

In your example, list is not an ArrayList.

private static ArrayList<T> list;

list is a static field of the Main class.

The volatile keyword only matters when one thread updates the field, and another thread subsequently accesses the field.

This line updates the list, but does not update the volatile field:

list.add(e);

After executing that line, the list has changed, but the field still refers to the same list object.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!