With the addition of a Thread constructor that can suppress the inheriting of inheritable thread-local variable initial values from the constructing thread [1], the usages of ManagedLocalsThread were removed from the base module. ManagedLocalsThread was also removed from the hierarchy of InnocuousThread, to help reduce dependencies. As a result InnocuousThread is no longer a ManagedLocalsThread, and the CleanerImpl should not use it in its instanceof check.
This just appears to be a timing issue as both changes were in flight. I did not add an explicit test as this is implementation detail, but adding one would be straight forward. Such a test may be more appropriate when 8146028 "Common Cleaner for finalization replacements in java.base" [2], is added. diff --git a/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java b/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java --- a/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java +++ b/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java @@ -39,7 +39,6 @@ import java.util.function.Function; import sun.misc.InnocuousThread; -import sun.misc.ManagedLocalsThread; /** * CleanerImpl manages a set of object references and corresponding cleaning actions. @@ -130,8 +129,8 @@ */ public void run() { Thread t = Thread.currentThread(); - ManagedLocalsThread mlThread = (t instanceof ManagedLocalsThread) - ? (ManagedLocalsThread) t + InnocuousThread mlThread = (t instanceof InnocuousThread) + ? (InnocuousThread) t : null; while (!phantomCleanableList.isListEmpty() || !weakCleanableList.isListEmpty() || -Chris. [1] https://bugs.openjdk.java.net/browse/JDK-8056152 [2] https://bugs.openjdk.java.net/browse/JDK-8146028