Matching C#'s volatile semantics with VarHandle
Jun 26, 2026A great thread explaining how volatile in Java and C# offer different memory ordering semantics. Java offers sequentially consistent semantics for volatile accesses, whereas C# offers acquire/release semantics.
I wondered if Java offered a way to weaken the volatile semantics to match C#’s. And it turned out it does: VarHandle. VarHandle allows you to have a finer control of the memory ordering of variables. I’ve set up a jcstress test to verify how many r1 = r2 = 0 we’d get on plain access, volatile access, and under acquire/release semantics. Here are the results:
| Variant | 0, 0 |
0, 1 |
1, 0 |
1, 1 |
0,0 verdict |
|---|---|---|---|---|---|
| Plain | 10,689,870 | 269,116,842 | 121,776,634 | 260 | ACCEPTABLE_INTERESTING |
| AcqRel | 9,383,746 | 218,844,266 | 116,334,950 | 65,882 | ACCEPTABLE_INTERESTING |
| Volatile | 0 | 173,087,216 | 74,618,180 | 517,792 | FORBIDDEN |
There’s not much difference between Plain and AcqRel, because our test isn’t able to capture that change. So I set up another litmus test
Litmus Test: Message Passing
Can this program see r1 = 1, r2 = 0?
// Thread 1 // Thread 2
x = 1 r1 = y
y = 1 r2 = x
as evidence that Plain is weaker than AcqRel, which in turn, from the first test, is weaker than Volatile. Results:
| Variant | 0, 0 |
0, 1 |
1, 0 |
1, 1 |
1,0 verdict |
|---|---|---|---|---|---|
| Plain | 14,804,668 | 42,286 | 6,410 | 399,332,668 | ACCEPTABLE_INTERESTING |
| AcqRel | 36,308,514 | 76,040 | 0 | 391,123,666 | FORBIDDEN |
| Volatile | 108,338,698 | 18,772 | 0 | 271,831,446 | FORBIDDEN |
TBH, I’m not an expert on the exact orderings allowed by each of these semantics. This post is just a reminder for me to be aware that ordering exists and can be fine-tuned.
#java #csharp #concurrency #memory-model #memory-ordering #jcstress