diff a/src/hotspot/share/runtime/objectMonitor.inline.hpp b/src/hotspot/share/runtime/objectMonitor.inline.hpp
--- a/src/hotspot/share/runtime/objectMonitor.inline.hpp
+++ b/src/hotspot/share/runtime/objectMonitor.inline.hpp
@@ -28,25 +28,30 @@
 #include "runtime/objectMonitor.hpp"
 
 #include "logging/log.hpp"
 #include "oops/access.inline.hpp"
 #include "runtime/atomic.hpp"
+#include "runtime/javaThread.inline.hpp"
 #include "runtime/lockStack.inline.hpp"
 #include "runtime/synchronizer.hpp"
+#include "runtime/threadIdentifier.hpp"
+
+inline void* ObjectMonitor::owner_for(JavaThread* thread) const {
+  int64_t tid = thread->lock_id();
+  assert(tid >= 3 && tid < ThreadIdentifier::current(), "must be reasonable");
+  return (void*)tid;
+}
 
 inline bool ObjectMonitor::is_entered(JavaThread* current) const {
-  if (LockingMode == LM_LIGHTWEIGHT) {
-    if (is_owner_anonymous()) {
+  if (is_owner_anonymous()) {
+    if (LockingMode == LM_LIGHTWEIGHT) {
       return current->lock_stack().contains(object());
     } else {
-      return current == owner_raw();
+      return current->is_lock_owned((address)stack_locker());
     }
   } else {
-    void* owner = owner_raw();
-    if (current == owner || current->is_lock_owned((address)owner)) {
-      return true;
-    }
+    return is_owner(current);
   }
   return false;
 }
 
 inline markWord ObjectMonitor::header() const {
@@ -78,10 +83,22 @@
 
 inline void* ObjectMonitor::owner_raw() const {
   return Atomic::load(&_owner);
 }
 
+inline BasicLock* ObjectMonitor::stack_locker() const {
+  return Atomic::load(&_stack_locker);
+}
+
+inline void ObjectMonitor::set_stack_locker(BasicLock* locker) {
+  Atomic::store(&_stack_locker, locker);
+}
+
+inline bool ObjectMonitor::is_stack_locker(JavaThread* current) {
+  return is_owner_anonymous() && current->is_lock_owned((address)stack_locker());
+}
+
 // Returns true if owner field == DEFLATER_MARKER and false otherwise.
 // This accessor is called when we really need to know if the owner
 // field == DEFLATER_MARKER and any non-null value won't do the trick.
 inline bool ObjectMonitor::owner_is_DEFLATER_MARKER() const {
   return owner_raw() == DEFLATER_MARKER;
@@ -107,11 +124,12 @@
   assert(has_owner(), "must be owned");
   _recursions = checked_cast<intx>(recursions);
 }
 
 // Clear _owner field; current value must match old_value.
-inline void ObjectMonitor::release_clear_owner(void* old_value) {
+inline void ObjectMonitor::release_clear_owner(JavaThread* old_owner) {
+  void* old_value = owner_for(old_owner);
 #ifdef ASSERT
   void* prev = Atomic::load(&_owner);
   assert(prev == old_value, "unexpected prev owner=" INTPTR_FORMAT
          ", expected=" INTPTR_FORMAT, p2i(prev), p2i(old_value));
 #endif
@@ -121,53 +139,63 @@
                                      p2i(this), p2i(old_value));
 }
 
 // Simply set _owner field to new_value; current value must match old_value.
 // (Simple means no memory sync needed.)
-inline void ObjectMonitor::set_owner_from(void* old_value, void* new_value) {
+inline void ObjectMonitor::set_owner_from_raw(void* old_value, void* new_value) {
 #ifdef ASSERT
   void* prev = Atomic::load(&_owner);
+  assert((int64_t)prev < ThreadIdentifier::current(), "must be reasonable");
   assert(prev == old_value, "unexpected prev owner=" INTPTR_FORMAT
          ", expected=" INTPTR_FORMAT, p2i(prev), p2i(old_value));
 #endif
   Atomic::store(&_owner, new_value);
   log_trace(monitorinflation, owner)("set_owner_from(): mid="
                                      INTPTR_FORMAT ", old_value=" INTPTR_FORMAT
                                      ", new_value=" INTPTR_FORMAT, p2i(this),
                                      p2i(old_value), p2i(new_value));
 }
 
+inline void ObjectMonitor::set_owner_from(void* old_value, JavaThread* current) {
+  set_owner_from_raw(old_value, owner_for(current));
+}
+
 // Simply set _owner field to self; current value must match basic_lock_p.
-inline void ObjectMonitor::set_owner_from_BasicLock(void* basic_lock_p, JavaThread* current) {
-#ifdef ASSERT
-  void* prev = Atomic::load(&_owner);
-  assert(prev == basic_lock_p, "unexpected prev owner=" INTPTR_FORMAT
-         ", expected=" INTPTR_FORMAT, p2i(prev), p2i(basic_lock_p));
-#endif
+inline void ObjectMonitor::set_owner_from_BasicLock(JavaThread* current) {
+  BasicLock* basic_lock_p = stack_locker();
+
+  set_stack_locker(nullptr); // first
+  assert(is_owner_anonymous(), "should be anon for now");
+
   // Non-null owner field to non-null owner field is safe without
   // cmpxchg() as long as all readers can tolerate either flavor.
-  Atomic::store(&_owner, current);
+  Atomic::store(&_owner, owner_for(current));
   log_trace(monitorinflation, owner)("set_owner_from_BasicLock(): mid="
                                      INTPTR_FORMAT ", basic_lock_p="
                                      INTPTR_FORMAT ", new_value=" INTPTR_FORMAT,
                                      p2i(this), p2i(basic_lock_p), p2i(current));
 }
 
 // Try to set _owner field to new_value if the current value matches
 // old_value. Otherwise, does not change the _owner field. Returns
 // the prior value of the _owner field.
-inline void* ObjectMonitor::try_set_owner_from(void* old_value, void* new_value) {
+inline void* ObjectMonitor::try_set_owner_from_raw(void* old_value, void* new_value) {
+  assert((int64_t)new_value < ThreadIdentifier::current(), "must be reasonable");
   void* prev = Atomic::cmpxchg(&_owner, old_value, new_value);
   if (prev == old_value) {
     log_trace(monitorinflation, owner)("try_set_owner_from(): mid="
                                        INTPTR_FORMAT ", prev=" INTPTR_FORMAT
                                        ", new=" INTPTR_FORMAT, p2i(this),
                                        p2i(prev), p2i(new_value));
   }
   return prev;
 }
 
+inline void* ObjectMonitor::try_set_owner_from(void* old_value, JavaThread* current) {
+  return try_set_owner_from_raw(old_value, owner_for(current));
+}
+
 // The _next_om field can be concurrently read and modified so we
 // use Atomic operations to disable compiler optimizations that
 // might try to elide loading and/or storing this field.
 
 // Simply get _next_om field.
