Reputation: 331
Creating the Array:
Uses the thread provided on startup since we only create the array once.
public static final Player[] PLAYERS = new Player[10];
Writing to Array:
Uses several different threads from a pool so that players can load concurrently.
public static Player loadPlayer(int freeIndex) {
//slow code that loads the player's saved data.
return PLAYERS[freeIndex] = player;
}
Reading from Array:
Uses a single thread to process players in order of their index.
public static void process() {
for(Player player : PLAYERS)
player.process();
}
Questions: Will players be immediately visible in method process after method loadPlayer is executed? If not (which I'm suspecting is the case) what approach should I take to fix the problem?
Upvotes: 1
Views: 560
Reputation: 2626
When you are loading player using different thread ,modifying shared collection.When a concurrent program is not correctly written, the errors tend to fall into one of the three categories: atomicity, visibility, or ordering.
To make your code thread safe you need to use lock or some other nonblocking collection like AtomicReferenceArray which will ensure thread visibility and thread safe.Also volatile variable is not ensure atomicity but variable will ensure visibility .
Atomicity deals with which actions and sets of actions have indivisible effects. This is the aspect of concurrency most familiar to programmers: it is usually thought of in terms of mutual exclusion. Visibility determines when the effects of one thread can be seen by another. Ordering determines when actions in one thread can be seen to occur out of order with respect to another
Upvotes: 1
Reputation: 28289
No, the value you set in one thread may not be available for another thread without synchronization. You can check here for more details about memory barrier.
In this case, you can use a CountDownLatch
for synchronization.
In initiation, create a CountDownLatch
:
public static final Player[] PLAYERS = new Player[10];
public static CountDownLatch lacth = new CountDownLatch(10);
After load a new Player
, call countDown
public static Player loadPlayer(int freeIndex) {
//slow code that loads the player's saved data.
PLAYERS[freeIndex] = player;
latch.countDown();
return player;
}
In the process thread, just wait. After loading all Player
, this
thread will be called up by the last countDown
thread:
public static void process() {
latch.await();
for(Player player : PLAYERS)
player.process();
}
Upvotes: 2
Reputation: 57202
You are correct that the players may not be immediately visible.
You can use an AtomicReferenceArray
instead of a standard array to ensure changes to players
are visible to other threads.
From the documentation,
The memory effects for accesses and updates of atomics generally follow the rules for volatiles, as stated in The Java Language Specification (17.4 Memory Model):
get has the memory effects of reading a volatile variable.
set has the memory effects of writing (assigning) a volatile variable.
lazySet has the memory effects of writing (assigning) a volatile variable except that it permits reorderings with subsequent (but not previous) memory actions that do not themselves impose reordering constraints with ordinary non-volatile writes. Among other usage contexts, lazySet may apply when nulling out, for the sake of garbage collection, a reference that is never accessed again.
weakCompareAndSet atomically reads and conditionally writes a variable but does not create any happens-before orderings, so provides no guarantees with respect to previous or subsequent reads and writes of any variables other than the target of the weakCompareAndSet. compareAndSet and all other read-and-update operations such as getAndIncrement have the memory effects of both reading and writing volatile variables.
Upvotes: 4