Because, as you say, i++ is not a singular operation. Normally we would say, it's not atomic. It really consists of 3 operations:
1. Read the current value of i
2. Add 1 to that value
3. Write the new value of i
If these steps occur simultaneously in different threads, strange things can happen.
Thread A: 1. read i = 0;
Thread A: 2. add 1 - Thread A's local copy of i is now 1
Thread B: 1. read i = 0;
Thread B: 2. add 1 - Thread B's local copy of i is now 1
Thread B: 3. write the new value of i = 1
Thread A: 3. write the new value of i = 1
That's probably what happened in the run you're asking about.
As a side note, there are processors where there is an opcode that increment to memory (and hence, is atomic) -- its just that the JVM doesn't have such a byte code... but in theory, it is possible to have a JIT that will optimize a set of byte codes to such an opcode, and hence, could possibly run with that instruction being atomic.
Also note that it is more than atomic concerns. There are also caching concerns. So, you will also need to declare "i" as volatile, or you may not see the increment, even if it is atomic.