Friday 6 December 2019

java - Does a synchronized block trigger a full memory fence for arrays?

Answer


Answer




I am confused about sharing arrays safely between threads in Java, specifically memory fences and the keyword synchronized.




This Q&A is helpful, but does not answer all of my questions: Java arrays: synchronized + Atomic*, or synchronized suffices?



What follows is sample code to demonstrate the issue. Assume there is a pool of worker threads that populates the SharedTable via method add(...). After all worker threads are done, a final thread reads and saves the data.



Sample code to demonstrate the issue:



public final class SharedTable {

// Column-oriented data entries

private final String[] data1Arr;
private final int[] data2Arr;
private final long[] data3Arr;
private final AtomicInteger nextIndex;

public SharedTable(int size) {
this.data1Arr = new String[size];
this.data2Arr = new int[size];
this.data3Arr = new long[size];
this.nextIndex = new AtomicInteger(0);

}

// Thread-safe: Called by worker threads
public void addEntry(String data1, int data2, long data3) {
final int index = nextIndex.getAndIncrement();
data1Arr[index] = data1;
data2Arr[index] = data2;
data3Arr[index] = data3;
}


// Not thread-safe: Called by clean-up/joiner/collator thread...
// after worker threads are complete
public void save() {
// Does this induce a full memory fence to ensure thread-safe reading of
synchronized (this) {
final int usedSide = nextIndex.get();
for (int i = 0; i < usedSide; ++i) {
final String data1 = data1Arr[i];
final int data2 = data2Arr[i];
final long data3 = data3Arr[i];

// TODO: Save data here
}
}
}
}


The sample code above could also be implemented using Atomic*Array, which acts as an "array of volatile values/references".



public final class SharedTable2 {


// Column-oriented data entries
private final AtomicReferenceArray data1Arr;
private final AtomicIntegerArray data2Arr;
private final AtomicLongArray data3Arr;
private final AtomicInteger nextIndex;

public SharedTable2(int size) { ... }

// Thread-safe: Called by worker threads

public void addEntry(String data1, int data2, long data3) {
final int index = nextIndex.getAndIncrement();
data1Arr.set(index, data1);
...
}

// Not thread-safe: Called by clean-up/joiner/collator thread...
// after worker threads are complete
public void save() {
final int usedSide = nextIndex.get();

for (int i = 0; i < usedSide; ++i) {
final String data1 = data1Arr.get(i);
final int data2 = data2Arr.get(i);
final long data3 = data3Arr.get(i);
// TODO: Save data here
}
}
}




  1. Is SharedTable thread-safe (and cache coherent)?

  2. Is SharedTable (much?) more efficient as only a single memory fence is required, whereas SharedTable2 invokes a memory fence for each call to Atomic*Array.set(...)?



If it helps, I am using Java 8 on 64-bit x86 hardware (Windows and Linux).


Answer



No, SharedTable is not thread-safe. A happens-before is only guaranteed if you read, from a synchronized block, something that has been written from a synchronized block using the same lock.



Since the writes are made out of a synchronized block, the JMM doesn't guarantee that the writes will be visible by the reader thread.



No comments:

Post a Comment

php - file_get_contents shows unexpected output while reading a file

I want to output an inline jpg image as a base64 encoded string, however when I do this : $contents = file_get_contents($filename); print &q...