Margus
Margus

Reputation: 20058

What is the use of AtomicReferenceArray?

When is it a good idea to use AtomicReferenceArray? Please explain with an example.

Upvotes: 15

Views: 14468

Answers (6)

Serge Rogatch
Serge Rogatch

Reputation: 15080

some notes from a C++ programmer below, please don't condemn my Java much :)

AtomicReferenceArray allows to avoid false sharing, when multiple CPU logical cores access the same cache line that is changed by one of the thread. Invalidating and re-fetching the cache is very expensive. Unfortunately there is no sizeof in Java, so we don't know how many bytes each AtomicReference takes, but assuming it's at least 8 bytes (the size of a pointer on 64-bit architectures), you can allocate as follows:

// a lower bound is enough
private final int sizeofAtomicReference = 8;
// good for x86/x64
private final int sizeofCacheLine = 64;
// the number of CPU cores
private final int nLogicalCores = Runtime.getRuntime().availableProcessors(); 
private final int refsPerCacheLine = (sizeofCacheLine + sizeofAtomicReference - 1) / sizeofAtomicReference;
private AtomicReferenceArray<Task> tasks = new AtomicReferenceArray<Task>(nLogicalCores *  refsPerCacheLine);

Now if you assign a task to i-th thread via

tasks.compareAndSet(i*refsPerCacheLine, null, new Task(/*problem definition here*/));

you guarantee that the task references are assigned to different CPU cache lines. Thus there is no expensive false sharing. So the latency of passing tasks from the producer thread to the consumer threads is minimal (for Java, but not for C++/Assembly).

Bonus: You then poll the tasks array in the worker threads like this:

// consider iWorker is the 0-based index of the logical core this thread is assigned to
final int myIndex = iWorker*refsPerCacheLine;
while(true) {
    Task curTask = tasks.get(myIndex);
    if(curTask == null) continue;
    if(curTask.isTerminator()) {
        return; // exit the thread
    }
    // ... Process the task here ...
    // Signal the producer thread that the current worker is free
    tasks.set(myIndex, null);
}

Upvotes: 1

Vy Do
Vy Do

Reputation: 52646

import java.util.concurrent.atomic.AtomicReferenceArray;

public class AtomicReferenceArrayExample {
    AtomicReferenceArray<String> arr = new AtomicReferenceArray<String>(10);

    public static void main(String... args) {
        new Thread(new AtomicReferenceArrayExample().new AddThread()).start();
        new Thread(new AtomicReferenceArrayExample().new AddThread()).start();
    }

    class AddThread implements Runnable {
        @Override
        public void run() {
            // Sets value at the index 1
            arr.set(0, "A");
            // At index 0, if current reference is "A" then it changes as "B".
            arr.compareAndSet(0, "A", "B");
            // At index 0, if current value is "B", then it is sets as "C".
            arr.weakCompareAndSet(0, "B", "C");
            System.out.println(arr.get(0));
        }
    }

}

//    Result:
//        C
//        C

Upvotes: 0

Chetan K
Chetan K

Reputation: 31

One possible use case would have been ConcurrentHashMap which extensively uses array internally. Array can be volatile but at per element level sematics can't be volatile. it's one of the reason automic array came into existence.

Upvotes: 2

starblue
starblue

Reputation: 56812

It could be useful if you have a large number of objects that are updated concurrently, for example in a large multiplayer game.

An update of reference i would follow the pattern

boolean success = false;
while (!success)
{
    E previous = atomicReferenceArray.get(i);
    E next = ... // compute updated object
    success = atomicReferenceArray.compareAndSet(i, previous, next);
}

Depending on the circumstances this may be faster and/or easier to use than locking (synchronized).

Upvotes: 5

dogbane
dogbane

Reputation: 274778

If you had a shared array of object references, then you would use an AtomicReferenceArray to ensure that the array couldn't be updated simultaneously by different threads i.e. only one element can be updated at a time.

However, in an AtomicReference[] (array of AtomicReference) multiple threads can still update different elements simulateously, because the atomicity is on the elements, not on the array as a whole.

More info here.

Upvotes: 11

irreputable
irreputable

Reputation: 45443

looks like it's functionally equivalent to AtomicReference[], occupying a little less memory though.

So it's useful when you need more than a million atomic references - can't think of any use case.

Upvotes: 13

Related Questions