I guess it's a "bit" late for the reply, but I guess I'll just add it here for some other desperate developers to read 
My understanding is that in JavaScript, your events run on the same thread (at least, from the developer's point of view). The event loop picks them up from the queue, and they run one after another, thus safely changing shared objects such as DOM.
You'd expect that the same thing is happening in Hubitat (well, I expected that for sure before I was disillusioned).
In Hubitat's case, the events are actually handled on different threads, and there is no synchronization of state between them. Both atomicState and state are not thread safe for this purpose - there's no guarantee that if one event is updating state, the other one will see the consistent update.
So you have to manually do this with tricky undocumented global synchronization objects like these (from this thread):
@Field static Object mutex = new Object()
def handler() {
synchronized (mutex) {
// Your code under real mutex
}
}
Which is frustrating.