Both threads are independent entities and have no synchronization between them. Then thread B executes and puts X, which is 1, into the thread B register. Then thread B increments its register to 2. Nothing is moved to memory X, so memory X stays the same. A and thread B will execute the three instructions one after the other, and the result would be 3, as expected.
Race conditions are usually difficult to discover, because they occur intermittently.
For example, if thread A takes a lock and updates the counter, it leaves memory X with a value of 2. A releases the lock, thread B takes the lock and updates the counter, taking 2 as its initial value for X and incrementing it to 3, the expected result. See Using Mutexes for more information about mutexes. Waiting for Threads Condition variables allow threads to block until some event or condition has occurred. Boolean predicates indicate whether the program has satisfied a condition variable.