diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp
index a9f6914e419..a8466c804fb 100644
--- a/src/hotspot/os/posix/os_posix.cpp
+++ b/src/hotspot/os/posix/os_posix.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -1533,15 +1533,22 @@ void os::PlatformEvent::unpark() {
 
 // JSR166 support
 
- os::PlatformParker::PlatformParker() {
-  int status;
-  status = pthread_cond_init(&_cond[REL_INDEX], _condAttr);
+ os::PlatformParker::PlatformParker() : _counter(0), _cur_index(-1) {
+  int status = pthread_cond_init(&_cond[REL_INDEX], _condAttr);
   assert_status(status == 0, status, "cond_init rel");
   status = pthread_cond_init(&_cond[ABS_INDEX], NULL);
   assert_status(status == 0, status, "cond_init abs");
   status = pthread_mutex_init(_mutex, _mutexAttr);
   assert_status(status == 0, status, "mutex_init");
-  _cur_index = -1; // mark as unused
+}
+
+os::PlatformParker::~PlatformParker() {
+  int status = pthread_cond_destroy(&_cond[REL_INDEX]);
+  assert_status(status == 0, status, "cond_destroy rel");
+  status = pthread_cond_destroy(&_cond[ABS_INDEX]);
+  assert_status(status == 0, status, "cond_destroy abs");
+  status = pthread_mutex_destroy(_mutex);
+  assert_status(status == 0, status, "mutex_destroy");
 }
 
 // Parker::park decrements count if > 0, else does a condvar wait.  Unpark
diff --git a/src/hotspot/os/posix/os_posix.hpp b/src/hotspot/os/posix/os_posix.hpp
index bc8dcd90af9..a93edaa11cb 100644
--- a/src/hotspot/os/posix/os_posix.hpp
+++ b/src/hotspot/os/posix/os_posix.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -184,21 +184,21 @@ class PlatformEvent : public CHeapObj<mtSynchronizer> {
 // API updates of course). But Parker methods use fastpaths that break that
 // level of encapsulation - so combining the two remains a future project.
 
-class PlatformParker : public CHeapObj<mtSynchronizer> {
+class PlatformParker {
+  NONCOPYABLE(PlatformParker);
  protected:
   enum {
     REL_INDEX = 0,
     ABS_INDEX = 1
   };
+  volatile int _counter;
   int _cur_index;  // which cond is in use: -1, 0, 1
   pthread_mutex_t _mutex[1];
   pthread_cond_t  _cond[2]; // one for relative times and one for absolute
 
- public:       // TODO-FIXME: make dtor private
-  ~PlatformParker() { guarantee(false, "invariant"); }
-
  public:
   PlatformParker();
+  ~PlatformParker();
 };
 
 // Workaround for a bug in macOSX kernel's pthread support (fixed in Mojave?).
diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp
index 9eed244f3f5..a64397e29e4 100644
--- a/src/hotspot/os/windows/os_windows.cpp
+++ b/src/hotspot/os/windows/os_windows.cpp
@@ -5431,11 +5431,10 @@ void os::PlatformEvent::unpark() {
 
 // The Windows implementation of Park is very straightforward: Basic
 // operations on Win32 Events turn out to have the right semantics to
-// use them directly. We opportunistically resuse the event inherited
-// from Monitor.
+// use them directly.
 
 void Parker::park(bool isAbsolute, jlong time) {
-  guarantee(_ParkEvent != NULL, "invariant");
+  guarantee(_ParkHandle != NULL, "invariant");
   // First, demultiplex/decode time arguments
   if (time < 0) { // don't wait
     return;
@@ -5457,16 +5456,16 @@ void Parker::park(bool isAbsolute, jlong time) {
 
   // Don't wait if interrupted or already triggered
   if (thread->is_interrupted(false) ||
-      WaitForSingleObject(_ParkEvent, 0) == WAIT_OBJECT_0) {
-    ResetEvent(_ParkEvent);
+      WaitForSingleObject(_ParkHandle, 0) == WAIT_OBJECT_0) {
+    ResetEvent(_ParkHandle);
     return;
   } else {
     ThreadBlockInVM tbivm(thread);
     OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
     thread->set_suspend_equivalent();
 
-    WaitForSingleObject(_ParkEvent, time);
-    ResetEvent(_ParkEvent);
+    WaitForSingleObject(_ParkHandle, time);
+    ResetEvent(_ParkHandle);
 
     // If externally suspended while waiting, re-suspend
     if (thread->handle_special_suspend_equivalent_condition()) {
@@ -5476,8 +5475,8 @@ void Parker::park(bool isAbsolute, jlong time) {
 }
 
 void Parker::unpark() {
-  guarantee(_ParkEvent != NULL, "invariant");
-  SetEvent(_ParkEvent);
+  guarantee(_ParkHandle != NULL, "invariant");
+  SetEvent(_ParkHandle);
 }
 
 // Platform Monitor implementation
diff --git a/src/hotspot/os/windows/os_windows.hpp b/src/hotspot/os/windows/os_windows.hpp
index ce677c249d2..a195af2c6d1 100644
--- a/src/hotspot/os/windows/os_windows.hpp
+++ b/src/hotspot/os/windows/os_windows.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -188,18 +188,21 @@ class PlatformEvent : public CHeapObj<mtSynchronizer> {
 
 
 
-class PlatformParker : public CHeapObj<mtSynchronizer> {
-  protected:
-    HANDLE _ParkEvent ;
+class PlatformParker {
+  NONCOPYABLE(PlatformParker);
 
-  public:
-    ~PlatformParker () { guarantee (0, "invariant") ; }
-    PlatformParker  () {
-      _ParkEvent = CreateEvent (NULL, true, false, NULL) ;
-      guarantee (_ParkEvent != NULL, "invariant") ;
-    }
+ protected:
+  HANDLE _ParkHandle;
 
-} ;
+ public:
+  PlatformParker() {
+    _ParkHandle = CreateEvent (NULL, true, false, NULL) ;
+    guarantee(_ParkHandle != NULL, "invariant") ;
+  }
+  ~PlatformParker() {
+    CloseHandle(_ParkHandle);
+  }
+};
 
 // Platform specific implementations that underpin VM Mutex/Monitor classes.
 // Note that CRITICAL_SECTION supports recursive locking, while the semantics
diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp
index 457e00ed659..eb2931c3be1 100644
--- a/src/hotspot/share/prims/unsafe.cpp
+++ b/src/hotspot/share/prims/unsafe.cpp
@@ -1001,8 +1001,6 @@ UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute,
 } UNSAFE_END
 
 UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread)) {
-  Parker* p = NULL;
-
   if (jthread != NULL) {
     ThreadsListHandle tlh;
     JavaThread* thr = NULL;
@@ -1012,18 +1010,13 @@ UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread))
       // This is a valid oop.
       if (thr != NULL) {
         // The JavaThread is alive.
-        p = thr->parker();
+        Parker* p = thr->parker();
+        HOTSPOT_THREAD_UNPARK((uintptr_t) p);
+        p->unpark();
       }
     }
   } // ThreadsListHandle is destroyed here.
 
-  // 'p' points to type-stable-memory if non-NULL. If the target
-  // thread terminates before we get here the new user of this
-  // Parker will get a 'spurious' unpark - which is perfectly valid.
-  if (p != NULL) {
-    HOTSPOT_THREAD_UNPARK((uintptr_t) p);
-    p->unpark();
-  }
 } UNSAFE_END
 
 UNSAFE_ENTRY(jint, Unsafe_GetLoadAverage0(JNIEnv *env, jobject unsafe, jdoubleArray loadavg, jint nelem)) {
diff --git a/src/hotspot/share/runtime/park.cpp b/src/hotspot/share/runtime/park.cpp
index 94326a602ce..1d88ca02388 100644
--- a/src/hotspot/share/runtime/park.cpp
+++ b/src/hotspot/share/runtime/park.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -112,55 +112,3 @@ void ParkEvent::operator delete (void * a) {
   // ParkEvents are type-stable and immortal ...
   ShouldNotReachHere();
 }
-
-
-// 6399321 As a temporary measure we copied & modified the ParkEvent::
-// allocate() and release() code for use by Parkers.  The Parker:: forms
-// will eventually be removed as we consolidate and shift over to ParkEvents
-// for both builtin synchronization and JSR166 operations.
-
-volatile int Parker::ListLock = 0 ;
-Parker * volatile Parker::FreeList = NULL ;
-
-Parker * Parker::Allocate (JavaThread * t) {
-  guarantee (t != NULL, "invariant") ;
-  Parker * p ;
-
-  // Start by trying to recycle an existing but unassociated
-  // Parker from the global free list.
-  // 8028280: using concurrent free list without memory management can leak
-  // pretty badly it turns out.
-  Thread::SpinAcquire(&ListLock, "ParkerFreeListAllocate");
-  {
-    p = FreeList;
-    if (p != NULL) {
-      FreeList = p->FreeNext;
-    }
-  }
-  Thread::SpinRelease(&ListLock);
-
-  if (p != NULL) {
-    guarantee (p->AssociatedWith == NULL, "invariant") ;
-  } else {
-    // Do this the hard way -- materialize a new Parker..
-    p = new Parker() ;
-  }
-  p->AssociatedWith = t ;          // Associate p with t
-  p->FreeNext       = NULL ;
-  return p ;
-}
-
-
-void Parker::Release (Parker * p) {
-  if (p == NULL) return ;
-  guarantee (p->AssociatedWith != NULL, "invariant") ;
-  guarantee (p->FreeNext == NULL      , "invariant") ;
-  p->AssociatedWith = NULL ;
-
-  Thread::SpinAcquire(&ListLock, "ParkerFreeListRelease");
-  {
-    p->FreeNext = FreeList;
-    FreeList = p;
-  }
-  Thread::SpinRelease(&ListLock);
-}
diff --git a/src/hotspot/share/runtime/park.hpp b/src/hotspot/share/runtime/park.hpp
index cf1cd97bdeb..0d46b04b486 100644
--- a/src/hotspot/share/runtime/park.hpp
+++ b/src/hotspot/share/runtime/park.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -27,17 +27,21 @@
 
 #include "utilities/debug.hpp"
 #include "utilities/globalDefinitions.hpp"
+
 /*
  * Per-thread blocking support for JSR166. See the Java-level
- * Documentation for rationale. Basically, park acts like wait, unpark
+ * documentation for rationale. Basically, park acts like wait, unpark
  * like notify.
  *
- * 6271289 --
- * To avoid errors where an os thread expires but the JavaThread still
- * exists, Parkers are immortal (type-stable) and are recycled across
- * new threads.  This parallels the ParkEvent implementation.
- * Because park-unpark allow spurious wakeups it is harmless if an
- * unpark call unparks a new thread using the old Parker reference.
+ * Parkers are inherently part of their associated JavaThread and are only
+ * accessed when the JavaThread is guaranteed to be alive (e.g. by operating
+ * on the current thread, or by having the thread protected by a
+ * ThreadsListHandle.
+ *
+ * Class Parker is declared in shared code and extends the platform-specific
+ * os::PlatformParker class, which contains the actual implementation
+ * mechanics (condvars/events etc). The implementation for park() and unpark()
+ * are also in the platform-specific os_<os>.cpp files.
  *
  * In the future we'll want to think about eliminating Parker and using
  * ParkEvent instead.  There's considerable duplication between the two
@@ -46,32 +50,15 @@
  */
 
 class Parker : public os::PlatformParker {
-private:
-  volatile int _counter ;
-  Parker * FreeNext ;
-  JavaThread * AssociatedWith ; // Current association
+ private:
+  NONCOPYABLE(Parker);
+ public:
+  Parker() : PlatformParker() {}
 
-public:
-  Parker() : PlatformParker() {
-    _counter       = 0 ;
-    FreeNext       = NULL ;
-    AssociatedWith = NULL ;
-  }
-protected:
-  ~Parker() { ShouldNotReachHere(); }
-public:
   // For simplicity of interface with Java, all forms of park (indefinite,
   // relative, and absolute) are multiplexed into one call.
   void park(bool isAbsolute, jlong time);
   void unpark();
-
-  // Lifecycle operators
-  static Parker * Allocate (JavaThread * t) ;
-  static void Release (Parker * e) ;
-private:
-  static Parker * volatile FreeList ;
-  static volatile int ListLock ;
-
 };
 
 /////////////////////////////////////////////////////////////
diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp
index 76f2de87a85..146356a8d08 100644
--- a/src/hotspot/share/runtime/thread.cpp
+++ b/src/hotspot/share/runtime/thread.cpp
@@ -1565,7 +1565,7 @@ JavaThread::JavaThread() :
   _should_post_on_exceptions_flag(JNI_FALSE),
   _thread_stat(new ThreadStatistics()),
 
-  _parker(Parker::Allocate(this)),
+  _parker(),
   _cached_monitor_info(nullptr),
 
   _class_to_be_initialized(nullptr),
@@ -1603,6 +1603,9 @@ JavaThread::JavaThread(bool is_attaching_via_jni) : JavaThread() {
 // interrupt support
 
 void JavaThread::interrupt() {
+  // All callers should have 'this' thread protected by a
+  // ThreadsListHandle so that it cannot terminate and deallocate
+  // itself.
   debug_only(check_for_dangling_thread_pointer(this);)
 
   // For Windows _interrupt_event
@@ -1700,10 +1703,6 @@ JavaThread::~JavaThread() {
   // Ask ServiceThread to release the threadObj OopHandle
   ServiceThread::add_oop_handle_release(_threadObj);
 
-  // JSR166 -- return the parker to the free list
-  Parker::Release(_parker);
-  _parker = NULL;
-
   // Return the sleep event to the free list
   ParkEvent::Release(_SleepEvent);
   _SleepEvent = NULL;
diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp
index 08e36e0a484..98ca2ffd41b 100644
--- a/src/hotspot/share/runtime/thread.hpp
+++ b/src/hotspot/share/runtime/thread.hpp
@@ -66,8 +66,6 @@ class JvmtiThreadState;
 class JvmtiVMObjectAllocEventCollector;
 class ThreadStatistics;
 class ConcurrentLocksDump;
-class ParkEvent;
-class Parker;
 class MonitorInfo;
 
 class BufferBlob;
@@ -1854,9 +1852,9 @@ class JavaThread: public Thread {
 
   // JSR166 per-thread parker
  private:
-  Parker*    _parker;
+  Parker _parker;
  public:
-  Parker*     parker() { return _parker; }
+  Parker* parker() { return &_parker; }
 
   // Biased locking support
  private: