3

My program looks like this:

public class Main {
    private static ArrayList<T> list;

    public static void main(String[] args) {
        new DataListener().start();
        new DataUpdater().start();
    }

    static class DataListener extends Thread {
        @Override
        public void run() {
            while(true){
                //Reading the ArrayList and displaying the updated data
                Thread.sleep(5000);
            }
        }
    }

    static class DataUpdater extends Thread{
        @Override
        public void run() {
            //Continuously receive data and update ArrayList;
        }
    }
}

In order to use this ArrayList in both threads, I know two options:

  1. To make the ArrayList volatile. However I read in this article that making variables volatile is only allowed if it "Writes to the variable do not depend on its current value." which I think in this case it does (because for example when you do an add operation on an ArrayList, the contents of the ArrayList after this operation depend on the current contents of the ArrayList, or doesn't it?). Also the DataUpdater has to remove some elements from the list every now and then, and I also read that editing a volatile variable from different threads is not possible.

  2. To make this ArrayList a synchronized variable. However, my DataUpdater will continuously update the ArrayList, so won't this block the DataListener from reading the ArrayList?

Did I misunderstand any concepts here or is there another option to make this possible?

Xander
  • 5,487
  • 14
  • 49
  • 77
  • Instead of an `ArrayList` you can use one of the queues from the `concurrency` package. – Titus Sep 13 '15 at 12:35
  • 1
    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_. – Solomon Slow Sep 14 '15 at 13:35

3 Answers3

9

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.

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
  • Yet, why do I need synchronization at all in this case? – Xander Sep 13 '15 at 13:34
  • 2
    @Xander Because operations on lists are not atomic. A read done in the middle of the other thread's update may see it in an inconsistent state. For example, if you delete an item in an ArrayList, all the other items are copied one place to the left. If your reader is reading it at that time, it may skip an item because of this. – RealSkeptic Sep 13 '15 at 14:35
  • Makes sense! Thank you! – Xander Sep 13 '15 at 14:39
  • If I make a copy of the list in the reader class, this problem is avoided as well, or isn't it? – Xander Sep 13 '15 at 14:40
  • 2
    @Xander No, because it needs to make the copy in a synchronized block and the writing should be synchronized on the same lock, as I said. Copying in itself is the same as reading the list using an iterator - and without synchronization you'll have the potential for skipped or duplicated items etc. (You're likely to get a `ConcurrentModificationException` when that happens). – RealSkeptic Sep 13 '15 at 14:53
2

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
}
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • That's another possibility, but it won't change much, since the OP only has one reader. – JB Nizet Sep 13 '15 at 12:33
  • Agree and what if OP selects ConcurrentSkipList? – SMA Sep 13 '15 at 12:36
  • There is no such class in the JDK. Anyway, I can't provide an optimal solution without knowing the exact use-case. All I know is that two threads must access a List concurrently. – JB Nizet Sep 13 '15 at 12:45
0
  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.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57