Reputation: 345
Suppose I declared the following array:
private final int[] array = new int[10];
Now, if I start 10 threads each writing a value to its own key (Thread 1 writes to array[0]
, Thread 2 writes to array[1]
, etc) will any thread try to cache array or all changes will be made in the main memory?
Upvotes: 1
Views: 258
Reputation: 533750
A thread can cache any non-volatile value.
In particular, code for a thread can inline into code a boolean
value that thread doesn't change. The thread might not detect a change until it is recompiled.
In this case, both the array reference and the values in the array will be cached, but most likely will see the new value after some time.
The use of final
makes no real difference. One reason is that reflection allows you to change final
fields.
A cleaner way to have a thread safe array like this is to use AtomicIntegerArray
added in Java 5.0
final AtomicIntegerArray array = new AtomicIntegerArray(10);
public void increment(int n) {
array.incrementAndGet(n);
}
Upvotes: 2
Reputation: 7164
The final
modifier makes sure all the Thread
-s can see the initialized value of that reference. This is important: the final
keyword refers only to the array, but not to the values stored within that array.
We don't know what is going to happen. The JVM might or might not cache the values. I suggest not to build any dependency on this.
I think you will want some visibility. In general, you can force visibility with either the volatile
keyword or with synchronization
. In this case you have an array if integers.
AtomicInteger
-sYou can replace it with array of AtomicInteger
-s:
private final AtomicInteger[] array = new AtomicInteger[10];
But in this case you'll have to manipulate the values somewhat differently:
array[0] = new AtomicInteger(0);
array[0].incrementAndGet();
Another approach is to use a concurrent list, like CopyOnWriteArrayList
or use the Collections.synchronizedList()
wrapper method:
// option 1
private final List<Integer> list = Collections.synchronizedList(new ArrayList<>());
// option 2
private final List<Integer> list = new CopyOnWriteArrayList<>();
Either way, you'll have somewhat different accessors than those of an array
. The List initialization is the most tricky part:
// this prevents all kinf of OutOfBoundsException-s
for (int i = 0; i < 10; i++) {
list.add(0);
}
// and now you can use it
list.set(0, list.get(0) + 1);
You can also use map, so you can skip the problematic initialization:
private final ConcurrentMap<Integer, Integer> map = new ConcurrentHashMap<>();
// ...
map.putIfAbsent(0, 0);
map.put(0, map.get(0) + 1);
Upvotes: 1
Reputation: 23339
No, final has no visibility semantics for individual array elements, even a volatile would only guarantee a happens before order on the reference mutation itself not the elements. For that you would need to synchronize or use a CopyOnWriteArrayList instead.
Upvotes: 1