diff --git a/src/hotspot/share/runtime/interfaceSupport.inline.hpp b/src/hotspot/share/runtime/interfaceSupport.inline.hpp
index b22c54e1f58..823edac96fc 100644
--- a/src/hotspot/share/runtime/interfaceSupport.inline.hpp
+++ b/src/hotspot/share/runtime/interfaceSupport.inline.hpp
@@ -150,8 +150,9 @@ class ThreadInVMForHandshake : public ThreadStateTransition {
 };
 
 class ThreadInVMfromJava : public ThreadStateTransition {
+  bool _check_asyncs;
  public:
-  ThreadInVMfromJava(JavaThread* thread) : ThreadStateTransition(thread) {
+  ThreadInVMfromJava(JavaThread* thread, bool check_asyncs = true) : ThreadStateTransition(thread), _check_asyncs(check_asyncs) {
     trans_from_java(_thread_in_vm);
   }
   ~ThreadInVMfromJava()  {
@@ -159,8 +160,9 @@ class ThreadInVMfromJava : public ThreadStateTransition {
       _thread->stack_overflow_state()->enable_stack_yellow_reserved_zone();
     }
     trans(_thread_in_vm, _thread_in_Java);
-    // Check for pending. async. exceptions or suspends.
-    if (_thread->has_special_runtime_exit_condition()) _thread->handle_special_runtime_exit_condition();
+    // We prevent asynchronous exceptions from being installed on return to Java in situations
+    // where we can't tolerate them. See bugs: 4324348, 4854693, 4998314, 5040492, 5050705.
+    if (_thread->has_special_runtime_exit_condition()) _thread->handle_special_runtime_exit_condition(_check_asyncs);
   }
 };
 
@@ -222,93 +224,45 @@ class ThreadToNativeFromVM : public ThreadStateTransition {
 };
 
 
+// Parameter in_flight_mutex_addr is only used by class Mutex to avoid certain deadlock
+// scenarios while making transitions that might block for a safepoint or handshake.
+// It's the address of a pointer to the mutex we are trying to acquire. This will be used to
+// access and release said mutex when transitioning back from blocked to vm (destructor) in
+// case we need to stop for a safepoint or handshake.
 class ThreadBlockInVM : public ThreadStateTransition {
- public:
-  ThreadBlockInVM(JavaThread *thread)
-  : ThreadStateTransition(thread) {
-    // Once we are blocked vm expects stack to be walkable
-    thread->frame_anchor()->make_walkable(thread);
-    trans(_thread_in_vm, _thread_blocked);
-  }
-  ~ThreadBlockInVM() {
-    trans(_thread_blocked, _thread_in_vm);
-    // We don't need to clear_walkable because it will happen automagically when we return to java
-  }
-};
-
-// Unlike ThreadBlockInVM, this class is designed to avoid certain deadlock scenarios while making
-// transitions inside class Mutex in cases where we need to block for a safepoint or handshake. It
-// receives an extra argument compared to ThreadBlockInVM, the address of a pointer to the mutex we
-// are trying to acquire. This will be used to access and release the mutex if needed to avoid
-// said deadlocks.
-// It works like ThreadBlockInVM but differs from it in two ways:
-// - When transitioning in (constructor), it checks for safepoints without blocking, i.e., calls
-//   back if needed to allow a pending safepoint to continue but does not block in it.
-// - When transitioning back (destructor), if there is a pending safepoint or handshake it releases
-//   the mutex that is only partially acquired.
-class ThreadBlockInVMWithDeadlockCheck : public ThreadStateTransition {
  private:
   Mutex** _in_flight_mutex_addr;
 
-  void release_mutex() {
-    assert(_in_flight_mutex_addr != NULL, "_in_flight_mutex_addr should have been set on constructor");
-    Mutex* in_flight_mutex = *_in_flight_mutex_addr;
-    if (in_flight_mutex != NULL) {
-      in_flight_mutex->release_for_safepoint();
-      *_in_flight_mutex_addr = NULL;
-    }
-  }
  public:
-  ThreadBlockInVMWithDeadlockCheck(JavaThread* thread, Mutex** in_flight_mutex_addr)
+  ThreadBlockInVM(JavaThread* thread, Mutex** in_flight_mutex_addr = NULL)
   : ThreadStateTransition(thread), _in_flight_mutex_addr(in_flight_mutex_addr) {
+    assert(thread->thread_state() == _thread_in_vm, "coming from wrong thread state");
+    thread->check_possible_safepoint();
     // Once we are blocked vm expects stack to be walkable
     thread->frame_anchor()->make_walkable(thread);
-
-    // All unsafe states are treated the same by the VMThread
-    // so we can skip the _thread_in_vm_trans state here. Since
-    // we don't read poll, it's enough to order the stores.
-    OrderAccess::storestore();
-
     thread->set_thread_state(_thread_blocked);
   }
-  ~ThreadBlockInVMWithDeadlockCheck() {
+  ~ThreadBlockInVM() {
+    assert(_thread->thread_state() == _thread_blocked, "coming from wrong thread state");
     // Change to transition state and ensure it is seen by the VM thread.
-    _thread->set_thread_state_fence((JavaThreadState)(_thread_blocked_trans));
+    _thread->set_thread_state_fence(_thread_blocked_trans);
 
     if (SafepointMechanism::should_process(_thread)) {
-      release_mutex();
+      if (_in_flight_mutex_addr != NULL) {
+        release_mutex();
+      }
       SafepointMechanism::process_if_requested(_thread);
     }
 
     _thread->set_thread_state(_thread_in_vm);
   }
-};
 
-
-// This special transition class is only used to prevent asynchronous exceptions
-// from being installed on vm exit in situations where we can't tolerate them.
-// See bugs: 4324348, 4854693, 4998314, 5040492, 5050705.
-class ThreadInVMfromJavaNoAsyncException : public ThreadStateTransition {
- public:
-  ThreadInVMfromJavaNoAsyncException(JavaThread* thread) : ThreadStateTransition(thread) {
-    trans_from_java(_thread_in_vm);
-  }
-  ~ThreadInVMfromJavaNoAsyncException()  {
-    if (_thread->stack_overflow_state()->stack_yellow_reserved_zone_disabled()) {
-      _thread->stack_overflow_state()->enable_stack_yellow_reserved_zone();
+  void release_mutex() {
+    Mutex* in_flight_mutex = *_in_flight_mutex_addr;
+    if (in_flight_mutex != NULL) {
+      in_flight_mutex->release_for_safepoint();
+      *_in_flight_mutex_addr = NULL;
     }
-    trans(_thread_in_vm, _thread_in_Java);
-    // NOTE: We do not check for pending. async. exceptions.
-    // If we did and moved the pending async exception over into the
-    // pending exception field, we would need to deopt (currently C2
-    // only). However, to do so would require that we transition back
-    // to the _thread_in_vm state. Instead we postpone the handling of
-    // the async exception.
-
-
-    // Check for pending. suspends only.
-    if (_thread->has_special_runtime_exit_condition())
-      _thread->handle_special_runtime_exit_condition(false);
   }
 };
 
@@ -383,7 +337,7 @@ class VMNativeEntryWrapper {
 
 #define JRT_ENTRY_NO_ASYNC(result_type, header)                      \
   result_type header {                                               \
-    ThreadInVMfromJavaNoAsyncException __tiv(thread);                \
+    ThreadInVMfromJava __tiv(thread, false /* check asyncs */);      \
     VM_ENTRY_BASE(result_type, header, thread)                       \
     debug_only(VMEntryWrapper __vew;)
 
@@ -401,7 +355,7 @@ class VMNativeEntryWrapper {
 
 #define JRT_BLOCK_NO_ASYNC                                           \
     {                                                                \
-    ThreadInVMfromJavaNoAsyncException __tiv(thread);                \
+    ThreadInVMfromJava __tiv(thread, false /* check asyncs */);      \
     Thread* THREAD = thread;                                         \
     debug_only(VMEntryWrapper __vew;)
 
diff --git a/src/hotspot/share/runtime/mutex.cpp b/src/hotspot/share/runtime/mutex.cpp
index fa969bb20e8..d32022c6405 100644
--- a/src/hotspot/share/runtime/mutex.cpp
+++ b/src/hotspot/share/runtime/mutex.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -84,12 +84,12 @@ void Mutex::lock_contended(Thread* self) {
     // Is it a JavaThread participating in the safepoint protocol.
     if (is_active_Java_thread) {
       assert(rank() > Mutex::special, "Potential deadlock with special or lesser rank mutex");
-      { ThreadBlockInVMWithDeadlockCheck tbivmdc(self->as_Java_thread(), &in_flight_mutex);
-        in_flight_mutex = this;  // save for ~ThreadBlockInVMWithDeadlockCheck
+      { ThreadBlockInVM tbivmdc(self->as_Java_thread(), &in_flight_mutex);
+        in_flight_mutex = this;  // save for ~ThreadBlockInVM
         _lock.lock();
       }
       if (in_flight_mutex != NULL) {
-        // Not unlocked by ~ThreadBlockInVMWithDeadlockCheck
+        // Not unlocked by ~ThreadBlockInVM
         break;
       }
     } else {
@@ -236,7 +236,7 @@ bool Monitor::wait(int64_t timeout, bool as_suspend_equivalent) {
   Mutex* in_flight_mutex = NULL;
 
   {
-    ThreadBlockInVMWithDeadlockCheck tbivmdc(self, &in_flight_mutex);
+    ThreadBlockInVM tbivmdc(self, &in_flight_mutex);
     OSThreadWaitState osts(self->osthread(), false /* not Object.wait() */);
     if (as_suspend_equivalent) {
       self->set_suspend_equivalent();
@@ -245,7 +245,7 @@ bool Monitor::wait(int64_t timeout, bool as_suspend_equivalent) {
     }
 
     wait_status = _lock.wait(timeout);
-    in_flight_mutex = this;  // save for ~ThreadBlockInVMWithDeadlockCheck
+    in_flight_mutex = this;  // save for ~ThreadBlockInVM
 
     // were we externally suspended while we were waiting?
     if (as_suspend_equivalent && self->handle_special_suspend_equivalent_condition()) {
@@ -260,7 +260,7 @@ bool Monitor::wait(int64_t timeout, bool as_suspend_equivalent) {
   }
 
   if (in_flight_mutex != NULL) {
-    // Not unlocked by ~ThreadBlockInVMWithDeadlockCheck
+    // Not unlocked by ~ThreadBlockInVM
     assert_owner(NULL);
     // Conceptually reestablish ownership of the lock.
     set_owner(self);
diff --git a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp
index 42870b7fef4..a22fe7fafdf 100644
--- a/src/hotspot/share/runtime/safepoint.cpp
+++ b/src/hotspot/share/runtime/safepoint.cpp
@@ -968,7 +968,7 @@ void ThreadSafepointState::handle_polling_page_exception() {
     // If we have a pending async exception deoptimize the frame
     // as otherwise we may never deliver it.
     if (self->has_async_condition()) {
-      ThreadInVMfromJavaNoAsyncException __tiv(self);
+      ThreadInVMfromJava __tiv(self, false /* check asyncs */);
       Deoptimization::deoptimize_frame(self, caller_fr.id());
     }