Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Immutable array thread-safety

I have a question regarding the Java Memory Model. Here is a simple class presenting the problem:

public class ImmutableIntArray {

    private final int[] array;

    public ImmutableIntArray() {
        array = new int[10];
        for (int i = 0; i < 10; i++) {
            array[i] = i;
        }
    }

    // Will always return the correct value?
    public int get(int index) {
        return array[index];
    }

}

As far as I know the JMM guarantees that the value of final fields will be visible to other threads after construction. But I want to ensure that other threads will see the most recent version of data stored in the array after construction.

Of course the code above is just a simple example presenting the problem, actually I want to implement a simple cache for direct byte buffers and I wouldn't like to rely on some Collection classes. Currently I am using a ReentrantReadWriteLock to ensure the correct behaviour but I would like avoid it if it is possible.

like image 561
Tamás Avatar asked Jul 08 '11 14:07

Tamás


2 Answers

In this example, everything will be fine (hmm, let's suspend judgement a bit). Immutability is ambrosia when it comes to thread-safety - if a value cannot change, the majority of concurrency problems are immediately no longer a concern.

Amir mentioned volatile which is generally useful - but the constructor also has similar semantics for final variables that ensure visibility. See JLS clause 17.5 for details - essentially the constructor forms a happens-before relationship between the write to the final variables and any subsequent reads.

EDIT: So you set the values reference to the array in the constructor, it's visible across all threads at that point, and then it doesn't change. So we know all other threads will see the same array. But what about the array's contents?

As it stands, array elements don't have any special semantics with regard to volatility, they're as if you just declared a class yourself something like:

public class ArrayTen {
    private int _0;
    private int _1;
    // ...
    private int _9;

    public int get(int index) {
       if (index == 0) return _0;
       // etc.
    }
}

So - another thread will only see these variables if we can do something to establish the happens-before relationship. And if my understanding is correct this requires but a small change to your original code.

We already know that the setting of the array reference happens-before the end of the constructor. An additional point which is always true, is that actions in one thread happen-before later actions in that same thread. So we can combine these by setting the array fields first, and then assigning the final field, so as to get this transitive guarantee of visibility. This will of course require a temporary variable:

public class ImmutableIntArray {

    private final int[] array;

    public ImmutableIntArray() {
        int[] tmp = new int[10];
        for (int i = 0; i < 10; i++) {
            tmp[i] = i;
        }
        array = tmp;
    }

    // get() etc.
}

I think this is guaranteed to be safe, now that we've switched the seemingly irrelevant order of assignment and population.

But again, there might be something else I've missed which means the concurrency guarantees aren't as robust as hoped. This question is to my mind an excellent example of why writing bulletproof multithreaded code is tricky, even when you think you're doing something very simple, and how it takes a lot of thought and caution (and then bugfixes) to get right.

like image 101
Andrzej Doyle Avatar answered Sep 28 '22 16:09

Andrzej Doyle


your example is not quite right. in order to get the final field assurance, you need:

public ImmutableIntArray() {
    int tmparray = new int[10];
    for (int i = 0; i < 10; i++) {
        tmparray[i] = i;
    }
    array = tmparray;
}
like image 31
jtahlborn Avatar answered Sep 28 '22 15:09

jtahlborn