First, storing polymorphic values in the map (either true values or OneShotThunks, depending on whether the Thunk has been unboxed or not) means you have to give up the static type safety of the compiler and cast values carefully. There's at least two bugs in the current version of the code due to unsafe casting. However, this problem can be fixed with careful coding and thorough tests.
Second, the given code doesn't actually prevent thunks from executing more than once. If the thunk throws an exception when executed, the thunk will still be kept in the map and marked as "unexecuted", so the next thread that tries to read that value will execute it again (and possibly throw an exception again). This is troubling both because the thunk executes multiple times and because an update from one thread can cause another thread to throw an exception. This is possibly fixable with some clever coding, but it's unclear what the desired semantics should be if the thunk throws an exception. (Possibly tombstone the value or replace it with the previous value: as if the update with the throwing thunk had never happened.)
But a fundamental flaw in the entire approach is that AtomicMap imposes a certain kind of threading model on client code. The above-mentioned behavior of exceptions propagating to other threads is one symptom of this problem, but there are others. For example, if the thunk takes a while to execute, or worse yet blocks (either momentarily or indefinitely), then other threads interacting with the same key in the map will block waiting for the thunk to finish executing. This behavior is perhaps fine for many applications, but if you want your threads to operate with strict timeouts for blocking operations, or if you want your threads to be entirely nonblocking, then you can't safely use AtomicMap. The implementation forces a particular threading model on client code, which is deprived from choosing its own threading model.
Perhaps a better interface for AtomicMap to implement is ConcurrentMap[A, Future[B]]. This alternative map could still make all its operations atomic, but by returning a Future[B] (instead of a straight-up B), it gives client code the power to check whether an exception was thrown or to block with a timeout when getting the value. Then its up to the client to decide what threading model it feels most comfortable with.
We have chosen to trade some flexibility in how the data structure can be used in exchange for an enormous increase in throughput. Your needs may differ, resulting in different trade offs.