In the absence of proper synchronization, this is indeed possible.
The Java language specification defines the semantics of multi-threaded Java programs in chapter 17. This chapter is pretty hard to understand, but it contains official rules you can rely on. In particular, he writes :
The memory model describes, given the program and the execution trace of this program, whether the execution trace is a legitimate program execution. The Java programming language memory model works by checking each read in the execution trace and verifying that the record observed by this read is valid according to certain rules.
The memory model describes the possible behavior of the program. An implementation can freely create any code that he likes, if all the programs arising as a result of execution lead to a result that can be predicted by the memory model.
To give an approximate overview, the memory model determines what happened before the relationship; any reordering should be consistent . The usual way of establishing occurs earlier than the actions performed by different threads is to synchronize these actions, for example, with a synchronized block or writing to or reading from a mutable variable.
In the absence of such synchronization, the runtime will execute threads independently of each other, allowing any reordering of the current thread it cannot observe.
That is, if you have a mutable sharing state, you usually need to synchronize the threads by accessing it.
source share