A good example could be String's hashcode:
private int hash; // Default to 0
public int hashCode() {
int h = hash;
if (h == 0 && count > 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
There is a data race here as hash can be written and read by different threads and there is no happens-before relationship (no synchronization).
However the program is sequentially consistent because it is impossible for a thread to see a hashcode which is not the actual hashcode of the string (when a thread executes the hashcode method, it can either see 0 and recalculate the value, which is deterministic, or it sees a valid value). This works because int writes are atomic.
EDIT
This (almost) same code is broken and could return a hashcode of 0:
public int hashCode() {
if (hash == 0 && count > 0) { //(1)
int h = hash;
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return hash; //(2)
}
as (1) and (2) could be reordered: (1) could read a non null value while (2) would read 0. That can't happen in the first example because the calculation is made on the local variable and the return value is also that local variable, which, by definition, is thread safe.
EDIT 2
Regarding your proposition C4, I don't think it is possible:
A program is correctly synchronized if and only if all sequentially consistent executions are free of data races.
If a program is correctly synchronized, then all executions of the program will appear to be sequentially consistent (§17.4.3).
So if a program is correctly synchronized:
- all the executions appear sequentially consistent.
- all sequentially consistent executions are free of data races
So we can conclude that all executions are free of data races and therefore the program is free of data races.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…