I was thinking about thread synchronization issues in compiled languages like C++, versus synchronization issues in languages like Java.
I'm wondering how a JVM language like Java doesn't (at least in practice) suffer from coredumps/segfaults/undefined behavior when race conditions occur.
Consider a C++ program with 2 threads. Suppose each thread shares a reference to an
std::vector<int>. Each thread continuously loops, and calls
std::vector<int>::push_back() - with no synchronization, no locking whatsoever. In this scenario, it's very likely the program will segfault immediately, at least on any mainstream platform/compiler. The reason is obvious: if one thread triggers a reallocation via a call to
push_back, and the other thread ends up writing to the old, free'd memory buffer, your program is likely to core dump immediately. Plus, the internal vector
capacity values may get corrupted, leading to all sorts of undefined behavior and crashing.
But this doesn't seem to happen in Java. Given the same scenario, where you have two threads, each with a reference to some unsynchronized data structure (like an
ArrayList<Integer>), and each thread calls
ArrayList<Integer>.add(), the worst that ever seems to happen in practice is an exception is thrown, probably an
ArrayIndexOutofBounds exception - and of course, the order of insertion is totally random.
I realize the JVM is just executing byte code (except when it's executing JIT'd code), but ultimately that byte code or JIT machine code needs to interact with actual system memory. I assume that Java automatically checks array indices with each access, and the language semantics and garbage collector ensure that all references are non-dangling - but with an unsynchronized data structure, when memory could literally be pulled out from under the program's feet by another thread, at any time, how is the JVM not segfaulting in this scenario unless it's somehow synchronizing memory reads/writes?