You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by st...@apache.org on 2025/11/21 10:21:53 UTC

(hadoop) branch trunk updated: HADOOP-19670. [JDK22] Replace Thread with SubjectPreservingThread (#8062)

This is an automated email from the ASF dual-hosted git repository.

stevel pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/hadoop.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 9cfbbf53172 HADOOP-19670. [JDK22] Replace Thread with SubjectPreservingThread (#8062)
9cfbbf53172 is described below

commit 9cfbbf531723529964bfc0dad9a7d26ae8b74fe9
Author: Istvan Toth <st...@apache.org>
AuthorDate: Fri Nov 21 11:21:46 2025 +0100

    HADOOP-19670. [JDK22] Replace Thread with SubjectPreservingThread (#8062)
    
    
    Replace uses of java.lang.Thread with org.apache.hadoop.util.concurrent.SubjectPreservingThread
    to restore pre JDK22 Subject behaviour in Threads
    
    This is needed to propagate UGI information down threads, so MUST be done in all new code too.
    
     Contributed by Istvan Toth
---
 .../security/authentication/util/SubjectUtil.java  |  3 +++
 .../org/apache/hadoop/conf/ReconfigurableBase.java |  6 ++++--
 .../org/apache/hadoop/fs/CachingGetSpaceUsed.java  |  3 ++-
 .../apache/hadoop/fs/DelegationTokenRenewer.java   |  5 +++--
 .../main/java/org/apache/hadoop/fs/FileSystem.java |  3 ++-
 .../java/org/apache/hadoop/ha/StreamPumper.java    |  3 ++-
 .../main/java/org/apache/hadoop/ipc/Client.java    |  7 +++---
 .../main/java/org/apache/hadoop/ipc/Server.java    | 18 +++++++++-------
 .../hadoop/metrics2/impl/MetricsSinkAdapter.java   |  7 +++---
 .../hadoop/net/unix/DomainSocketWatcher.java       |  3 ++-
 .../hadoop/security/UserGroupInformation.java      |  3 ++-
 .../AbstractDelegationTokenSecretManager.java      |  6 +++---
 .../service/launcher/InterruptEscalator.java       |  3 ++-
 .../org/apache/hadoop/util/AsyncDiskService.java   |  3 ++-
 .../util/BlockingThreadPoolExecutorService.java    |  3 ++-
 .../java/org/apache/hadoop/util/GcTimeMonitor.java |  6 ++++--
 .../main/java/org/apache/hadoop/util/Shell.java    |  5 +++--
 .../apache/hadoop/util/ShutdownHookManager.java    |  5 +++--
 .../util/concurrent/SubjectInheritingThread.java   |  4 ++--
 .../org/apache/hadoop/conf/TestConfiguration.java  |  9 ++++----
 .../apache/hadoop/conf/TestReconfiguration.java    |  3 ++-
 .../org/apache/hadoop/fs/FCStatisticsBaseTest.java |  5 +++--
 .../apache/hadoop/fs/TestFileSystemCaching.java    |  6 +++---
 .../test/java/org/apache/hadoop/fs/TestTrash.java  |  7 +++---
 .../hadoop/fs/loadGenerator/LoadGenerator.java     |  5 +++--
 .../java/org/apache/hadoop/io/TestMD5Hash.java     |  9 ++++----
 .../test/java/org/apache/hadoop/io/TestText.java   |  5 +++--
 .../apache/hadoop/io/nativeio/TestNativeIO.java    |  6 ++++--
 .../hadoop/io/nativeio/TestNativeIoInit.java       | 17 ++++++++-------
 .../apache/hadoop/io/retry/TestFailoverProxy.java  |  9 ++++----
 .../java/org/apache/hadoop/ipc/TestAsyncIPC.java   | 13 +++++------
 .../apache/hadoop/ipc/TestCallQueueManager.java    |  9 ++++----
 .../org/apache/hadoop/ipc/TestFairCallQueue.java   |  5 +++--
 .../test/java/org/apache/hadoop/ipc/TestIPC.java   | 17 ++++++++-------
 .../apache/hadoop/ipc/TestIPCServerResponder.java  |  5 +++--
 .../hadoop/ipc/TestProtoBufRpcServerHandoff.java   |  5 +++--
 .../test/java/org/apache/hadoop/ipc/TestRPC.java   |  3 ++-
 .../org/apache/hadoop/ipc/TestRPCWaitForProxy.java |  5 +++--
 .../apache/hadoop/ipc/TestRpcServerHandoff.java    |  5 +++--
 .../org/apache/hadoop/ipc/TestSocketFactory.java   |  3 ++-
 .../apache/hadoop/metrics2/impl/TestSinkQueue.java |  9 ++++----
 .../hadoop/metrics2/lib/TestMutableMetrics.java    |  7 +++---
 .../hadoop/metrics2/source/TestJvmMetrics.java     |  5 +++--
 .../apache/hadoop/net/unix/TestDomainSocket.java   | 19 ++++++++--------
 .../hadoop/net/unix/TestDomainSocketWatcher.java   |  9 ++++----
 .../hadoop/security/TestAuthorizationContext.java  |  3 ++-
 .../apache/hadoop/security/TestGroupsCaching.java  | 13 +++++------
 .../hadoop/security/TestLdapGroupsMapping.java     |  5 +++--
 .../hadoop/security/TestUserGroupInformation.java  |  5 +++--
 .../hadoop/service/TestServiceLifecycle.java       |  3 ++-
 .../launcher/testservices/RunningService.java      |  3 ++-
 .../apache/hadoop/test/MultithreadedTestUtil.java  |  5 +++--
 .../hadoop/test/TestTimedOutTestsListener.java     |  5 +++--
 .../apache/hadoop/util/TestAutoCloseableLock.java  | 10 +++++----
 .../apache/hadoop/util/TestInstrumentedLock.java   | 11 +++++-----
 .../hadoop/util/TestInstrumentedReadWriteLock.java | 17 ++++++++-------
 .../org/apache/hadoop/util/TestPureJavaCrc32.java  |  7 +++---
 .../apache/hadoop/util/TestReflectionUtils.java    |  7 +++---
 .../java/org/apache/hadoop/util/TestShell.java     |  9 ++++----
 .../hadoop/util/TestShutdownThreadsHelper.java     |  3 ++-
 .../hadoop/crypto/key/kms/server/KMSBenchmark.java |  5 +++--
 .../hadoop/registry/server/dns/RegistryDNS.java    |  3 ++-
 .../server/services/RegistryAdminService.java      |  3 ++-
 .../org/apache/hadoop/hdfs/DeadNodeDetector.java   |  5 +++--
 .../hadoop/hdfs/util/CombinedHostsFileReader.java  |  3 ++-
 .../ha/TestRequestHedgingProxyProvider.java        |  5 +++--
 .../hadoop/hdfs/util/TestByteArrayManager.java     | 11 +++++-----
 .../hadoop/hdfs/nfs/nfs3/AsyncDataService.java     |  3 ++-
 .../org/apache/hadoop/hdfs/nfs/TestUdpServer.java  |  9 ++++----
 .../federation/resolver/order/RouterResolver.java  |  3 ++-
 .../federation/router/ConnectionManager.java       |  5 +++--
 .../router/MountTableRefresherThread.java          |  5 +++--
 .../hdfs/server/federation/router/Router.java      |  5 +++--
 .../federation/router/RouterHeartbeatService.java  |  3 ++-
 .../server/federation/router/RouterRpcServer.java  |  3 ++-
 .../TestRouterRefreshFairnessPolicyController.java |  7 +++---
 .../router/TestRouterFederationRename.java         |  3 ++-
 .../router/TestRouterMountTableCacheRefresh.java   |  2 +-
 .../server/federation/router/TestRouterRpc.java    |  3 ++-
 .../federation/router/async/utils/SyncClass.java   |  4 +++-
 .../hdfs/server/blockmanagement/BlockManager.java  |  6 +++---
 .../blockmanagement/CacheReplicationMonitor.java   |  5 +++--
 .../server/blockmanagement/SlowDiskTracker.java    |  3 ++-
 .../apache/hadoop/hdfs/server/common/Storage.java  |  5 +++--
 .../hdfs/server/datanode/BPServiceActor.java       |  9 ++++----
 .../hadoop/hdfs/server/datanode/DataNode.java      |  5 +++--
 .../hadoop/hdfs/server/datanode/VolumeScanner.java |  5 +++--
 .../fsdataset/impl/FsDatasetAsyncDiskService.java  |  3 ++-
 .../datanode/fsdataset/impl/FsVolumeList.java      |  9 ++++----
 .../impl/RamDiskAsyncLazyPersistService.java       |  3 ++-
 .../hdfs/server/namenode/FSEditLogAsync.java       |  3 ++-
 .../hadoop/hdfs/server/namenode/FSImage.java       |  3 ++-
 .../server/namenode/FSImageFormatProtobuf.java     |  5 +++--
 .../hadoop/hdfs/server/namenode/NameNode.java      |  3 ++-
 .../hdfs/server/namenode/ha/EditLogTailer.java     |  5 +++--
 .../server/namenode/ha/StandbyCheckpointer.java    |  5 +++--
 .../hadoop/hdfs/TestAppendSnapshotTruncate.java    |  3 ++-
 .../TestClientProtocolForPipelineRecovery.java     |  7 +++---
 .../apache/hadoop/hdfs/TestDFSClientRetries.java   | 15 +++++++------
 .../apache/hadoop/hdfs/TestDFSOutputStream.java    |  5 +++--
 .../java/org/apache/hadoop/hdfs/TestDFSShell.java  |  5 +++--
 .../org/apache/hadoop/hdfs/TestDatanodeDeath.java  |  9 ++++----
 .../apache/hadoop/hdfs/TestDeadNodeDetection.java  |  3 ++-
 .../org/apache/hadoop/hdfs/TestDecommission.java   |  3 ++-
 .../hadoop/hdfs/TestDecommissionWithStriped.java   |  7 +++---
 .../org/apache/hadoop/hdfs/TestFileAppend2.java    |  5 +++--
 .../org/apache/hadoop/hdfs/TestFileAppend3.java    |  9 ++++----
 .../org/apache/hadoop/hdfs/TestFileAppend4.java    |  9 ++++----
 .../hadoop/hdfs/TestFileConcurrentReader.java      |  9 ++++----
 .../apache/hadoop/hdfs/TestFileCreationClient.java |  5 +++--
 .../hadoop/hdfs/TestMultiThreadedHflush.java       |  9 ++++----
 .../apache/hadoop/hdfs/TestParallelReadUtil.java   |  5 +++--
 .../test/java/org/apache/hadoop/hdfs/TestRead.java |  3 ++-
 .../TestReplaceDatanodeFailureReplication.java     |  5 +++--
 .../hadoop/hdfs/TestReplaceDatanodeOnFailure.java  |  5 +++--
 .../hdfs/client/impl/TestBlockReaderFactory.java   |  7 +++---
 .../hdfs/server/balancer/TestBalancerService.java  |  3 ++-
 .../server/blockmanagement/TestBlockManager.java   |  3 ++-
 .../hdfs/server/datanode/BlockReportTestBase.java  |  5 +++--
 .../hdfs/server/datanode/TestBPOfferService.java   |  3 ++-
 .../hdfs/server/datanode/TestBlockRecovery.java    |  5 +++--
 .../hdfs/server/datanode/TestBlockRecovery2.java   |  5 +++--
 .../datanode/TestDataNodeHotSwapVolumes.java       |  7 +++---
 .../server/datanode/TestDataNodeVolumeFailure.java |  3 ++-
 .../server/datanode/TestDataSetLockManager.java    |  3 ++-
 .../datanode/TestDataXceiverBackwardsCompat.java   |  3 ++-
 .../server/datanode/TestSimulatedFSDataset.java    |  5 +++--
 .../datanode/fsdataset/impl/TestFsDatasetImpl.java | 17 ++++++++-------
 .../datanode/fsdataset/impl/TestFsVolumeList.java  |  5 +++--
 .../fsdataset/impl/TestLazyPersistFiles.java       |  3 ++-
 .../impl/TestReplicaCachingGetSpaceUsed.java       |  5 +++--
 .../hdfs/server/diskbalancer/TestDiskBalancer.java |  7 +++---
 .../server/namenode/NNThroughputBenchmark.java     |  5 +++--
 .../hdfs/server/namenode/TestAuditLogger.java      |  5 +++--
 .../hdfs/server/namenode/TestCheckpoint.java       |  5 +++--
 .../hdfs/server/namenode/TestDeleteRace.java       | 15 +++++++------
 .../hadoop/hdfs/server/namenode/TestEditLog.java   |  3 ++-
 .../hdfs/server/namenode/TestEditLogRace.java      | 11 +++++-----
 .../hdfs/server/namenode/TestFSNamesystemLock.java | 15 +++++++------
 .../server/namenode/TestFSNamesystemMBean.java     |  5 +++--
 .../hdfs/server/namenode/TestFileTruncate.java     |  3 ++-
 .../server/namenode/TestLargeDirectoryDelete.java  |  5 +++--
 .../hdfs/server/namenode/TestListOpenFiles.java    |  3 ++-
 .../hadoop/hdfs/server/namenode/TestMetaSave.java  |  5 +++--
 .../server/namenode/TestReencryptionHandler.java   |  5 +++--
 .../server/namenode/TestSecurityTokenEditLog.java  |  3 ++-
 .../server/namenode/ha/TestBootstrapStandby.java   |  5 +++--
 .../namenode/ha/TestConsistentReadsObserver.java   | 11 +++++-----
 .../namenode/ha/TestDelegationTokensWithHA.java    |  5 +++--
 .../hdfs/server/namenode/ha/TestHASafeMode.java    |  5 +++--
 .../server/namenode/ha/TestRetryCacheWithHA.java   |  5 +++--
 .../server/namenode/ha/TestStandbyCheckpoints.java |  5 +++--
 .../snapshot/TestOpenFilesWithSnapshot.java        |  3 ++-
 .../shortcircuit/TestShortCircuitLocalRead.java    |  7 +++---
 .../hadoop/hdfs/util/TestReferenceCountMap.java    |  9 ++++----
 .../apache/hadoop/hdfs/web/TestWebHDFSForHA.java   |  5 +++--
 .../hadoop/hdfs/web/TestWebHdfsTimeouts.java       |  5 +++--
 .../hadoop/mapred/LocalContainerLauncher.java      |  3 ++-
 .../jobhistory/JobHistoryEventHandler.java         |  3 ++-
 .../hadoop/mapreduce/v2/app/MRAppMaster.java       |  5 +++--
 .../mapreduce/v2/app/TaskHeartbeatHandler.java     |  3 ++-
 .../v2/app/commit/CommitterEventHandler.java       |  5 +++--
 .../v2/app/launcher/ContainerLauncherImpl.java     |  5 +++--
 .../hadoop/mapreduce/v2/app/rm/RMCommunicator.java |  3 ++-
 .../mapreduce/v2/app/rm/RMContainerAllocator.java  |  7 +++---
 .../v2/app/speculate/DefaultSpeculator.java        |  3 ++-
 .../hadoop/mapreduce/v2/app/MRAppBenchmark.java    |  3 ++-
 .../v2/app/local/TestLocalContainerAllocator.java  |  3 ++-
 .../org/apache/hadoop/mapred/LocalJobRunner.java   |  5 +++--
 .../org/apache/hadoop/mapred/CleanupQueue.java     |  5 +++--
 .../java/org/apache/hadoop/mapred/MapTask.java     |  5 +++--
 .../main/java/org/apache/hadoop/mapred/Task.java   |  5 +++--
 .../apache/hadoop/mapred/pipes/Application.java    |  5 +++--
 .../apache/hadoop/mapred/pipes/BinaryProtocol.java |  5 +++--
 .../apache/hadoop/mapreduce/lib/chain/Chain.java   |  9 ++++----
 .../mapreduce/lib/map/MultithreadedMapper.java     |  5 +++--
 .../hadoop/mapreduce/task/reduce/EventFetcher.java |  5 +++--
 .../hadoop/mapreduce/task/reduce/Fetcher.java      |  5 +++--
 .../hadoop/mapreduce/task/reduce/LocalFetcher.java |  2 +-
 .../hadoop/mapreduce/task/reduce/MergeThread.java  |  5 +++--
 .../task/reduce/ShuffleSchedulerImpl.java          |  5 +++--
 .../apache/hadoop/mapreduce/util/ProcessTree.java  |  5 +++--
 .../org/apache/hadoop/mapred/TestIndexCache.java   | 17 ++++++++-------
 .../mapred/TestLocatedFileStatusFetcher.java       |  5 +++--
 .../hadoop/mapred/TestTaskProgressReporter.java    |  7 +++---
 .../mapreduce/v2/hs/TestHistoryFileManager.java    |  9 ++++----
 .../mapreduce/v2/hs/TestJobHistoryEvents.java      |  3 ++-
 .../TestUnnecessaryBlockingOnHistoryFileInfo.java  |  5 +++--
 .../test/java/org/apache/hadoop/FailingMapper.java |  5 +++--
 .../java/org/apache/hadoop/fs/JHLogAnalyzer.java   |  5 +++--
 .../hadoop/fs/loadGenerator/LoadGeneratorMR.java   |  5 +++--
 .../org/apache/hadoop/mapred/ReliabilityTest.java  | 13 +++++------
 .../java/org/apache/hadoop/mapred/TestCollect.java |  5 +++--
 .../hadoop/mapred/jobcontrol/TestJobControl.java   |  3 ++-
 .../mapred/jobcontrol/TestLocalJobControl.java     |  3 ++-
 .../hadoop/mapred/pipes/TestPipeApplication.java   |  4 ++--
 .../apache/hadoop/mapreduce/TestLocalRunner.java   |  5 +++--
 .../lib/jobcontrol/TestMapReduceJobControl.java    |  3 ++-
 .../TestMapReduceJobControlWithMocks.java          |  3 ++-
 .../hadoop/mapreduce/v2/MiniMRYarnCluster.java     |  5 +++--
 .../mapred/nativetask/StatusReportChecker.java     |  3 ++-
 .../hadoop/examples/terasort/TeraInputFormat.java  |  5 +++--
 .../hadoop/fs/s3a/ITestS3AIOStatisticsContext.java |  5 +++--
 .../hadoop/fs/s3a/scale/ITestS3AConcurrentOps.java |  3 ++-
 .../azure/AzureFileSystemThreadPoolExecutor.java   |  3 ++-
 .../hadoop/fs/azure/BlockBlobAppendStream.java     |  3 ++-
 .../apache/hadoop/fs/azure/SelfRenewingLease.java  |  3 ++-
 .../fs/azure/metrics/BandwidthGaugeUpdater.java    |  3 ++-
 .../fs/azurebfs/services/ListActionTaker.java      |  3 ++-
 .../fs/azurebfs/services/ReadBufferManagerV1.java  |  3 ++-
 .../fs/azurebfs/services/ReadBufferManagerV2.java  |  3 ++-
 .../fs/azure/ITestAzureConcurrentOutOfBandIo.java  |  3 ++-
 ...emOperationsExceptionHandlingMultiThreaded.java | 25 +++++++++++-----------
 .../fs/azure/ITestNativeAzureFileSystemLive.java   |  7 +++---
 .../fs/azure/NativeAzureFileSystemBaseTest.java    |  5 +++--
 .../TestNativeAzureFileSystemConcurrency.java      |  3 ++-
 .../azure/metrics/TestBandwidthGaugeUpdater.java   |  3 ++-
 .../azurebfs/ITestAzureBlobFileSystemAppend.java   |  3 ++-
 .../azurebfs/ITestAzureBlobFileSystemRename.java   |  5 +++--
 .../fs/compat/common/HdfsCompatShellScope.java     |  5 +++--
 .../hadoop/tools/util/TestProducerConsumer.java    |  9 ++++----
 .../tools/dynamometer/ApplicationMaster.java       |  3 ++-
 .../apache/hadoop/tools/dynamometer/Client.java    |  3 ++-
 .../hadoop/tools/dynamometer/DynoInfraUtils.java   |  3 ++-
 .../tools/dynamometer/TestDynamometerInfra.java    |  3 ++-
 .../workloadgenerator/audit/AuditReplayThread.java |  5 +++--
 .../procedure/BalanceProcedureScheduler.java       | 13 +++++------
 .../org/apache/hadoop/mapred/gridmix/Gridmix.java  |  5 +++--
 .../apache/hadoop/mapred/gridmix/JobMonitor.java   |  5 +++--
 .../org/apache/hadoop/mapred/gridmix/LoadJob.java  |  9 ++++----
 .../hadoop/mapred/gridmix/ReplayJobFactory.java    |  5 +++--
 .../hadoop/mapred/gridmix/SerialJobFactory.java    |  5 +++--
 .../apache/hadoop/mapred/gridmix/Statistics.java   |  5 +++--
 .../hadoop/mapred/gridmix/StressJobFactory.java    |  5 +++--
 .../resourceestimator/service/ShutdownHook.java    |  5 +++--
 .../org/apache/hadoop/streaming/PipeMapRed.java    |  9 ++++----
 .../distributedshell/ApplicationMaster.java        |  3 ++-
 .../distributedshell/DistributedShellBaseTest.java |  3 ++-
 .../distributedshell/TestDSAppMaster.java          |  3 ++-
 .../distributedshell/TestDSTimelineV20.java        |  5 +++--
 .../TestDSWithMultipleNodeManager.java             |  7 +++---
 .../unmanagedamlauncher/UnmanagedAMLauncher.java   |  9 ++++----
 .../service/client/SystemServiceManagerImpl.java   |  3 ++-
 .../hadoop/yarn/service/ClientAMService.java       |  5 +++--
 .../yarn/service/utils/TestServiceApiUtil.java     |  5 +++--
 .../yarn/client/api/ContainerShellWebSocket.java   |  3 ++-
 .../client/api/async/impl/AMRMClientAsyncImpl.java |  9 ++++----
 .../client/api/async/impl/NMClientAsyncImpl.java   |  7 +++---
 .../org/apache/hadoop/yarn/client/cli/TopCLI.java  |  7 +++---
 .../hadoop/yarn/client/ProtocolHATestBase.java     |  5 +++--
 .../TestFederationRMFailoverProxyProvider.java     |  3 ++-
 .../apache/hadoop/yarn/client/TestGetGroups.java   |  5 +++--
 .../TestHedgingRequestRMFailoverProxyProvider.java |  5 +++--
 .../apache/hadoop/yarn/client/TestRMFailover.java  |  5 +++--
 ...eManagerAdministrationProtocolPBClientImpl.java |  5 +++--
 .../client/api/async/impl/TestNMClientAsync.java   |  5 +++--
 .../yarn/client/api/impl/TestYarnClient.java       |  5 +++--
 .../apache/hadoop/yarn/event/AsyncDispatcher.java  |  5 +++--
 .../apache/hadoop/yarn/event/EventDispatcher.java  |  3 ++-
 .../yarn/util/AbstractLivelinessMonitor.java       |  3 ++-
 .../yarn/TestYarnUncaughtExceptionHandler.java     |  7 +++---
 .../logaggregation/TestAggregatedLogFormat.java    |  5 +++--
 .../yarn/util/TestProcfsBasedProcessTree.java      |  5 +++--
 .../yarn/server/timeline/LeveldbTimelineStore.java |  5 +++--
 .../timeline/RollingLevelDBTimelineStore.java      |  5 +++--
 .../yarn/server/AMHeartbeatRequestHandler.java     |  5 +++--
 .../yarn/server/uam/UnmanagedAMPoolManager.java    |  3 ++-
 .../uam/TestUnmanagedApplicationManager.java       |  7 +++---
 .../yarn/server/nodemanager/ContainerExecutor.java |  5 +++--
 .../yarn/server/nodemanager/NodeManager.java       |  9 ++++----
 .../nodemanager/NodeResourceMonitorImpl.java       |  5 +++--
 .../server/nodemanager/NodeStatusUpdaterImpl.java  |  5 +++--
 .../WindowsSecureContainerExecutor.java            |  5 +++--
 .../containermanager/container/ContainerImpl.java  |  5 +++--
 .../resources/CGroupElasticMemoryController.java   |  5 +++--
 .../localizer/ResourceLocalizationService.java     | 13 +++++------
 .../monitor/ContainersMonitorImpl.java             |  9 ++++----
 .../nodemanager/TestLinuxContainerExecutor.java    |  5 +++--
 .../server/nodemanager/TestNodeManagerResync.java  |  5 +++--
 .../server/nodemanager/TestNodeStatusUpdater.java  |  3 ++-
 .../amrmproxy/TestableFederationInterceptor.java   |  4 ++--
 .../localizer/TestContainerLocalizer.java          |  9 ++++----
 .../util/TestCgroupsLCEResourcesHandler.java       |  5 +++--
 .../server/resourcemanager/ResourceManager.java    |  5 +++--
 .../amlauncher/ApplicationMasterLauncher.java      |  5 +++--
 .../federation/FederationStateStoreService.java    |  3 ++-
 .../metrics/TimelineServiceV1Publisher.java        |  5 +++--
 .../resourcemanager/monitor/SchedulingMonitor.java |  3 ++-
 .../resourcemanager/recovery/ZKRMStateStore.java   |  5 +++--
 .../rmapp/attempt/RMAppAttemptImpl.java            |  5 +++--
 .../scheduler/AbstractYarnScheduler.java           |  5 +++--
 .../scheduler/activities/ActivitiesManager.java    |  3 ++-
 .../scheduler/capacity/CapacityScheduler.java      |  9 ++++----
 .../fair/AllocationFileLoaderService.java          |  3 ++-
 .../scheduler/fair/FSPreemptionThread.java         |  5 +++--
 .../scheduler/fair/FairScheduler.java              |  5 +++--
 .../scheduler/placement/MultiNodeSorter.java       |  3 ++-
 .../security/DelegationTokenRenewer.java           |  7 +++---
 .../yarn/server/resourcemanager/ACLsTestBase.java  |  5 +++--
 .../resourcemanager/TestApplicationACLs.java       |  5 +++--
 .../resourcemanager/TestClientRMService.java       |  5 +++--
 .../resourcemanager/TestLeaderElectorService.java  |  5 +++--
 .../yarn/server/resourcemanager/TestRMHA.java      |  3 ++-
 .../resourcemanager/TestRMHAForAsyncScheduler.java | 18 +++++++++-------
 .../recovery/TestFSRMStateStore.java               |  3 ++-
 .../TestZKRMStateStoreZKClientConnections.java     |  5 +++--
 .../resourcetracker/TestNMExpiry.java              |  5 +++--
 .../scheduler/TestQueueMetrics.java                |  5 +++--
 .../scheduler/capacity/TestCapacityScheduler.java  |  9 ++++----
 .../TestCapacitySchedulerAsyncScheduling.java      |  5 +++--
 .../capacity/TestCapacitySchedulerMultiNodes.java  |  5 +++--
 ...tCapacitySchedulerMultiNodesWithPreemption.java |  5 +++--
 .../scheduler/capacity/TestLeafQueue.java          |  7 +++---
 .../fair/FairSchedulerWithMockPreemption.java      |  2 +-
 .../scheduler/fair/TestContinuousScheduling.java   |  5 +++--
 .../TestDominantResourceFairnessPolicy.java        |  5 +++--
 .../security/TestDelegationTokenRenewer.java       |  5 +++--
 .../TestRMWebServicesSchedulerActivities.java      |  5 +++--
 .../apache/hadoop/yarn/server/router/Router.java   |  3 ++-
 .../router/clientrm/TestRouterClientRMService.java |  5 +++--
 .../router/rmadmin/TestRouterRMAdminService.java   |  5 +++--
 .../router/webapp/TestRouterWebServices.java       |  5 +++--
 .../cosmosdb/CosmosDBDocumentStoreReader.java      |  3 ++-
 .../cosmosdb/CosmosDBDocumentStoreWriter.java      |  3 ++-
 324 files changed, 1061 insertions(+), 737 deletions(-)

diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SubjectUtil.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SubjectUtil.java
index 67c3be1f7f8..c94f554e3dc 100644
--- a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SubjectUtil.java
+++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SubjectUtil.java
@@ -61,6 +61,9 @@ public final class SubjectUtil {
   private static final int JAVA_SPEC_VER = Math.max(8,
       Integer.parseInt(System.getProperty("java.specification.version").split("\\.")[0]));
 
+  /**
+   * True if the current JVM copies the current JAAS subject into new threads automatically.
+   */
   public static final boolean THREAD_INHERITS_SUBJECT = checkThreadInheritsSubject();
 
   /**
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ReconfigurableBase.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ReconfigurableBase.java
index 1c451ca6d30..0ca13ad0d79 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ReconfigurableBase.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/conf/ReconfigurableBase.java
@@ -22,6 +22,7 @@
 import org.apache.hadoop.util.Preconditions;
 import org.apache.hadoop.thirdparty.com.google.common.collect.Maps;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.conf.ReconfigurationUtil.PropertyChange;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -105,15 +106,16 @@ public Collection<PropertyChange> getChangedProperties(
   /**
    * A background thread to apply configuration changes.
    */
-  private static class ReconfigurationThread extends Thread {
+  private static class ReconfigurationThread extends SubjectInheritingThread {
     private ReconfigurableBase parent;
 
     ReconfigurationThread(ReconfigurableBase base) {
+      super();
       this.parent = base;
     }
 
     // See {@link ReconfigurationServlet#applyChanges}
-    public void run() {
+    public void work() {
       LOG.info("Starting reconfiguration task.");
       final Configuration oldConf = parent.getConf();
       final Configuration newConf = parent.getNewConf();
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CachingGetSpaceUsed.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CachingGetSpaceUsed.java
index d7b61346d4e..43e7121dc26 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CachingGetSpaceUsed.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/CachingGetSpaceUsed.java
@@ -20,6 +20,7 @@
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
 import org.apache.hadoop.classification.VisibleForTesting;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -107,7 +108,7 @@ void init() {
    */
   private void initRefreshThread(boolean runImmediately) {
     if (refreshInterval > 0) {
-      refreshUsed = new Thread(new RefreshThread(this, runImmediately),
+      refreshUsed = new SubjectInheritingThread(new RefreshThread(this, runImmediately),
           "refreshUsed-" + dirPath);
       refreshUsed.setDaemon(true);
       refreshUsed.start();
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/DelegationTokenRenewer.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/DelegationTokenRenewer.java
index 794855508c6..0892db697d7 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/DelegationTokenRenewer.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/DelegationTokenRenewer.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.security.token.TokenIdentifier;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -38,7 +39,7 @@
  */
 @InterfaceAudience.Private
 public class DelegationTokenRenewer
-    extends Thread {
+    extends SubjectInheritingThread {
   private static final Logger LOG = LoggerFactory
       .getLogger(DelegationTokenRenewer.class);
 
@@ -263,7 +264,7 @@ public <T extends FileSystem & Renewable> void removeRenewAction(
   }
 
   @Override
-  public void run() {
+  public void work() {
     for(;;) {
       RenewAction<?> action = null;
       try {
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java
index 930abf0b5d1..957cac07d97 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java
@@ -81,6 +81,7 @@
 import org.apache.hadoop.util.ReflectionUtils;
 import org.apache.hadoop.util.ShutdownHookManager;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.tracing.Tracer;
 import org.apache.hadoop.tracing.TraceScope;
 import org.apache.hadoop.util.Preconditions;
@@ -4087,7 +4088,7 @@ private interface StatisticsAggregator<T> {
     static {
       STATS_DATA_REF_QUEUE = new ReferenceQueue<>();
       // start a single daemon cleaner thread
-      STATS_DATA_CLEANER = new Thread(new StatisticsDataReferenceCleaner());
+      STATS_DATA_CLEANER = new SubjectInheritingThread(new StatisticsDataReferenceCleaner());
       STATS_DATA_CLEANER.
           setName(StatisticsDataReferenceCleaner.class.getName());
       STATS_DATA_CLEANER.setDaemon(true);
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ha/StreamPumper.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ha/StreamPumper.java
index 12a24fd079e..86ec95c14e3 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ha/StreamPumper.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ha/StreamPumper.java
@@ -17,6 +17,7 @@
  */
 package org.apache.hadoop.ha;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 
 import java.io.BufferedReader;
@@ -50,7 +51,7 @@ enum StreamType {
     this.stream = stream;
     this.type = type;
     
-    thread = new Thread(new Runnable() {
+    thread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Client.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Client.java
index 5caf27edcda..b036caedfa3 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Client.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Client.java
@@ -54,6 +54,7 @@
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
 import org.apache.hadoop.util.concurrent.AsyncGet;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.tracing.Span;
 import org.apache.hadoop.tracing.Tracer;
 import org.slf4j.Logger;
@@ -407,7 +408,7 @@ public synchronized void setRpcResponse(Writable rpcResponse) {
   /** Thread that reads responses and notifies callers.  Each connection owns a
    * socket connected to a remote address.  Calls are multiplexed through this
    * socket: responses may be delivered out of order. */
-  private class Connection extends Thread {
+  private class Connection extends SubjectInheritingThread {
     private InetSocketAddress server;             // server ip:port
     private final ConnectionId remoteId;          // connection id
     private AuthMethod authMethod; // authentication method
@@ -448,7 +449,7 @@ private class Connection extends Thread {
         Consumer<Connection> removeMethod) {
       this.remoteId = remoteId;
       this.server = remoteId.getAddress();
-      this.rpcRequestThread = new Thread(new RpcRequestSender(),
+      this.rpcRequestThread = new SubjectInheritingThread(new RpcRequestSender(),
           "IPC Parameter Sending Thread for " + remoteId);
       this.rpcRequestThread.setDaemon(true);
 
@@ -1126,7 +1127,7 @@ private synchronized void sendPing() throws IOException {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         // Don't start the ipc parameter sending thread until we start this
         // thread, because the shutdown logic only gets triggered if this
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Server.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Server.java
index f0ed1d4a97f..3d23588ff64 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Server.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Server.java
@@ -124,6 +124,8 @@
 import org.apache.hadoop.util.ProtoUtil;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
+
 import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.hadoop.tracing.Span;
 import org.apache.hadoop.tracing.SpanContext;
@@ -1471,7 +1473,7 @@ public String toString() {
   }
 
   /** Listens on the socket. Creates jobs for the handler threads*/
-  private class Listener extends Thread {
+  private class Listener extends SubjectInheritingThread {
     
     private ServerSocketChannel acceptChannel = null; //the accept channel
     private Selector selector = null; //the selector that we use for the server
@@ -1520,7 +1522,7 @@ void setIsAuxiliary() {
       this.isOnAuxiliaryPort = true;
     }
     
-    private class Reader extends Thread {
+    private class Reader extends SubjectInheritingThread {
       final private BlockingQueue<Connection> pendingConnections;
       private final Selector readSelector;
 
@@ -1533,7 +1535,7 @@ private class Reader extends Thread {
       }
       
       @Override
-      public void run() {
+      public void work() {
         LOG.info("Starting " + Thread.currentThread().getName());
         try {
           doRunLoop();
@@ -1612,7 +1614,7 @@ void shutdown() {
     }
 
     @Override
-    public void run() {
+    public void work() {
       LOG.info(Thread.currentThread().getName() + ": starting");
       SERVER.set(Server.this);
       connectionManager.startIdleScan();
@@ -1760,7 +1762,7 @@ Reader getReader() {
   }
 
   // Sends responses of RPC back to clients.
-  private class Responder extends Thread {
+  private class Responder extends SubjectInheritingThread {
     private final Selector writeSelector;
     private int pending;         // connections waiting to register
 
@@ -1772,7 +1774,7 @@ private class Responder extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       LOG.info(Thread.currentThread().getName() + ": starting");
       SERVER.set(Server.this);
       try {
@@ -3219,7 +3221,7 @@ private void internalQueueCall(Call call, boolean blocking)
   }
 
   /** Handles queued calls . */
-  private class Handler extends Thread {
+  private class Handler extends SubjectInheritingThread {
     public Handler(int instanceNumber) {
       this.setDaemon(true);
       this.setName("IPC Server handler "+ instanceNumber +
@@ -3227,7 +3229,7 @@ public Handler(int instanceNumber) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       LOG.debug("{}: starting", Thread.currentThread().getName());
       SERVER.set(Server.this);
       while (running) {
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/impl/MetricsSinkAdapter.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/impl/MetricsSinkAdapter.java
index c8843f2812e..ae2c890fa1c 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/impl/MetricsSinkAdapter.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/impl/MetricsSinkAdapter.java
@@ -34,6 +34,7 @@
 import org.apache.hadoop.metrics2.MetricsFilter;
 import org.apache.hadoop.metrics2.MetricsSink;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -48,7 +49,7 @@ class MetricsSinkAdapter implements SinkQueue.Consumer<MetricsBuffer> {
   private final MetricsSink sink;
   private final MetricsFilter sourceFilter, recordFilter, metricFilter;
   private final SinkQueue<MetricsBuffer> queue;
-  private final Thread sinkThread;
+  private final SubjectInheritingThread sinkThread;
   private volatile boolean stopping = false;
   private volatile boolean inError = false;
   private final int periodMs, firstRetryDelay, retryCount;
@@ -84,8 +85,8 @@ class MetricsSinkAdapter implements SinkQueue.Consumer<MetricsBuffer> {
                                   "Dropped updates per sink", 0);
     qsize = registry.newGauge("Sink_"+ name + "Qsize", "Queue size", 0);
 
-    sinkThread = new Thread() {
-      @Override public void run() {
+    sinkThread = new SubjectInheritingThread() {
+      @Override public void work() {
         publishMetricsFromQueue();
       }
     };
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/unix/DomainSocketWatcher.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/unix/DomainSocketWatcher.java
index 5c8a3357a3e..9cfd5e0c1d5 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/unix/DomainSocketWatcher.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/unix/DomainSocketWatcher.java
@@ -36,6 +36,7 @@
 import org.apache.hadoop.util.NativeCodeLoader;
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.util.Preconditions;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.Uninterruptibles;
 import org.slf4j.Logger;
@@ -440,7 +441,7 @@ private void sendCallbackAndRemove(String caller,
   }
 
   @VisibleForTesting
-  final Thread watcherThread = new Thread(new Runnable() {
+  final Thread watcherThread = new SubjectInheritingThread(new Runnable() {
     @Override
     public void run() {
       if (LOG.isDebugEnabled()) {
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java
index b6be569026f..2f0933782d8 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java
@@ -89,6 +89,7 @@
 import org.apache.hadoop.util.Shell;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -930,7 +931,7 @@ private void executeAutoRenewalTask(final String userName,
                   new ThreadFactory() {
                     @Override
                     public Thread newThread(Runnable r) {
-                      Thread t = new Thread(r);
+                      Thread t = new SubjectInheritingThread(r);
                       t.setDaemon(true);
                       t.setName("TGT Renewer for " + userName);
                       return t;
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/AbstractDelegationTokenSecretManager.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/AbstractDelegationTokenSecretManager.java
index 9cf3ccdd445..194042948bd 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/AbstractDelegationTokenSecretManager.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/delegation/AbstractDelegationTokenSecretManager.java
@@ -60,7 +60,7 @@
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.util.Daemon;
 import org.apache.hadoop.util.Time;
-
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.util.Preconditions;
 import org.apache.hadoop.util.functional.InvocationRaisingIOE;
 import org.slf4j.Logger;
@@ -912,12 +912,12 @@ public boolean isRunning() {
     return running;
   }
 
-  private class ExpiredTokenRemover extends Thread {
+  private class ExpiredTokenRemover extends SubjectInheritingThread {
     private long lastMasterKeyUpdate;
     private long lastTokenCacheCleanup;
 
     @Override
-    public void run() {
+    public void work() {
       LOG.info("Starting expired delegation token remover thread, "
           + "tokenRemoverScanInterval=" + tokenRemoverScanInterval
           / (60 * 1000) + " min(s)");
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/service/launcher/InterruptEscalator.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/service/launcher/InterruptEscalator.java
index 4d43c3a106f..9400f9590b2 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/service/launcher/InterruptEscalator.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/service/launcher/InterruptEscalator.java
@@ -24,6 +24,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.hadoop.util.Preconditions;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -116,7 +117,7 @@ public void interrupted(IrqHandler.InterruptData interruptData) {
       //start an async shutdown thread with a timeout
       ServiceForcedShutdown shutdown =
           new ServiceForcedShutdown(service, shutdownTimeMillis);
-      Thread thread = new Thread(shutdown);
+      Thread thread = new SubjectInheritingThread(shutdown);
       thread.setDaemon(true);
       thread.setName("Service Forced Shutdown");
       thread.start();
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/AsyncDiskService.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/AsyncDiskService.java
index a3bf4faf0a9..6c9157a8a70 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/AsyncDiskService.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/AsyncDiskService.java
@@ -28,6 +28,7 @@
 
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -74,7 +75,7 @@ public AsyncDiskService(String[] volumes) {
     threadFactory = new ThreadFactory() {
       @Override
       public Thread newThread(Runnable r) {
-        return new Thread(threadGroup, r);
+        return new SubjectInheritingThread(threadGroup, r);
       }
     };
     
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/BlockingThreadPoolExecutorService.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/BlockingThreadPoolExecutorService.java
index 5c90e4bd2d6..c7b249444bc 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/BlockingThreadPoolExecutorService.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/BlockingThreadPoolExecutorService.java
@@ -29,6 +29,7 @@
 import org.slf4j.LoggerFactory;
 
 import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 /**
  * This ExecutorService blocks the submission of new tasks when its queue is
@@ -71,7 +72,7 @@ static ThreadFactory getNamedThreadFactory(final String prefix) {
       public Thread newThread(Runnable r) {
         final String name =
             prefix + "-pool" + poolNum + "-t" + threadNumber.getAndIncrement();
-        return new Thread(group, r, name);
+        return new SubjectInheritingThread(group, r, name);
       }
     };
   }
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/GcTimeMonitor.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/GcTimeMonitor.java
index 95d0d4d290c..b27fed0fa65 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/GcTimeMonitor.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/GcTimeMonitor.java
@@ -23,13 +23,15 @@
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
+
 /**
  * This class monitors the percentage of time the JVM is paused in GC within
  * the specified observation window, say 1 minute. The user can provide a
  * hook which will be called whenever this percentage exceeds the specified
  * threshold.
  */
-public class GcTimeMonitor extends Thread {
+public class GcTimeMonitor extends SubjectInheritingThread {
 
   private final long maxGcTimePercentage;
   private final long observationWindowMs, sleepIntervalMs;
@@ -151,7 +153,7 @@ public GcTimeMonitor(long observationWindowMs, long sleepIntervalMs,
   }
 
   @Override
-  public void run() {
+  public void work() {
     startTime = System.currentTimeMillis();
     curData.timestamp = startTime;
     gcDataBuf[startIdx].setValues(startTime, 0);
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java
index c53ddc0725c..775f3b34edb 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java
@@ -38,6 +38,7 @@
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -1020,9 +1021,9 @@ private void runCommand() throws IOException {
 
     // read error and input streams as this would free up the buffers
     // free the error stream buffer
-    Thread errThread = new Thread() {
+    Thread errThread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           String line = errReader.readLine();
           while((line != null) && !isInterrupted()) {
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/ShutdownHookManager.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/ShutdownHookManager.java
index e85f850514b..07d8fb07ac1 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/ShutdownHookManager.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/ShutdownHookManager.java
@@ -20,6 +20,7 @@
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
 import org.apache.hadoop.util.concurrent.HadoopExecutors;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -84,9 +85,9 @@ public final class ShutdownHookManager {
   static {
     try {
       Runtime.getRuntime().addShutdownHook(
-        new Thread() {
+        new SubjectInheritingThread() {
           @Override
-          public void run() {
+          public void work() {
             if (MGR.shutdownInProgress.getAndSet(true)) {
               LOG.info("Shutdown process invoked a second time: ignoring");
               return;
diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/concurrent/SubjectInheritingThread.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/concurrent/SubjectInheritingThread.java
index e9b6745340d..afb4c5bb36e 100644
--- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/concurrent/SubjectInheritingThread.java
+++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/concurrent/SubjectInheritingThread.java
@@ -40,8 +40,8 @@
  * Thread.
  * <p>
  * {@link #run()} cannot be directly overridden, as that would also override the
- * subject restoration logic. SubjectInheritingThread provides a {@link work()}
- * method instead, which is wrapped and invoked by its own final {@link run()}
+ * subject restoration logic. SubjectInheritingThread provides a {@link #work()}
+ * method instead, which is wrapped and invoked by its own final {@link #run()}
  * method.
  */
 public class SubjectInheritingThread extends Thread {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestConfiguration.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestConfiguration.java
index 6bb7c2bd1d3..56ec25ebd8d 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestConfiguration.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestConfiguration.java
@@ -81,6 +81,7 @@
 import org.apache.hadoop.security.alias.CredentialProviderFactory;
 import org.apache.hadoop.security.alias.LocalJavaKeyStoreProvider;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
 
@@ -2478,7 +2479,7 @@ public void testConcurrentAccesses() throws Exception {
     Configuration conf = new Configuration();
     conf.addResource(fileResource);
 
-    class ConfigModifyThread extends Thread {
+    class ConfigModifyThread extends SubjectInheritingThread {
       final private Configuration config;
       final private String prefix;
 
@@ -2488,7 +2489,7 @@ public ConfigModifyThread(Configuration conf, String prefix) {
       }
 
       @Override
-      public void run() {
+      public void work() {
         for (int i = 0; i < 10000; i++) {
           config.set("some.config.value-" + prefix + i, "value");
         }
@@ -2740,7 +2741,7 @@ private static Configuration checkCDATA(byte[] bytes) {
   @Test
   public void testConcurrentModificationDuringIteration() throws InterruptedException {
     Configuration configuration = new Configuration();
-    new Thread(() -> {
+    new SubjectInheritingThread(() -> {
       while (true) {
         configuration.set(String.valueOf(Math.random()), String.valueOf(Math.random()));
       }
@@ -2748,7 +2749,7 @@ public void testConcurrentModificationDuringIteration() throws InterruptedExcept
 
     AtomicBoolean exceptionOccurred = new AtomicBoolean(false);
 
-    new Thread(() -> {
+    new SubjectInheritingThread(() -> {
       while (true) {
         try {
           configuration.iterator();
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestReconfiguration.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestReconfiguration.java
index c475f7c826b..3f089a59d0f 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestReconfiguration.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/conf/TestReconfiguration.java
@@ -22,6 +22,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Lists;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.conf.ReconfigurationUtil.PropertyChange;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -284,7 +285,7 @@ public void testReconfigure() {
   public void testThread() throws ReconfigurationException { 
     ReconfigurableDummy dummy = new ReconfigurableDummy(conf1);
     assertTrue(dummy.getConf().get(PROP1).equals(VAL1));
-    Thread dummyThread = new Thread(dummy);
+    Thread dummyThread = new SubjectInheritingThread(dummy);
     dummyThread.start();
     try {
       Thread.sleep(500);
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/FCStatisticsBaseTest.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/FCStatisticsBaseTest.java
index 80edbeab4c8..aa50f0d9f49 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/FCStatisticsBaseTest.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/FCStatisticsBaseTest.java
@@ -40,6 +40,7 @@
 
 import java.util.function.Supplier;
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.Uninterruptibles;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -73,9 +74,9 @@ public void testStatisticsOperations() throws Exception {
     stats.incrementWriteOps(123);
     assertEquals(123, stats.getWriteOps());
     
-    Thread thread = new Thread() {
+    SubjectInheritingThread thread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         stats.incrementWriteOps(1);
       }
     };
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileSystemCaching.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileSystemCaching.java
index 119bad41a30..491387efa25 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileSystemCaching.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileSystemCaching.java
@@ -36,7 +36,7 @@
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.ListenableFuture;
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.ListeningExecutorService;
 import org.apache.hadoop.util.BlockingThreadPoolExecutorService;
-
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 
 import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.FS_CREATION_PARALLEL_COUNT;
@@ -125,9 +125,9 @@ public void initialize(URI uri, Configuration conf) throws IOException {
   @Test
   public void testCacheEnabledWithInitializeForeverFS() throws Exception {
     final Configuration conf = new Configuration();
-    Thread t = new Thread() {
+    SubjectInheritingThread t = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         conf.set("fs.localfs1.impl", "org.apache.hadoop.fs." +
          "TestFileSystemCaching$InitializeForeverFileSystem");
         try {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestTrash.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestTrash.java
index 89d7419f763..a1241a384d4 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestTrash.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestTrash.java
@@ -49,6 +49,7 @@
 import org.apache.hadoop.fs.permission.FsPermission;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 /**
  * This class tests commands from Trash.
@@ -724,7 +725,7 @@ public void testTrashEmptier() throws Exception {
 
     // Start Emptier in background
     Runnable emptier = trash.getEmptier();
-    Thread emptierThread = new Thread(emptier);
+    Thread emptierThread = new SubjectInheritingThread(emptier);
     emptierThread.start();
 
     FsShell shell = new FsShell();
@@ -792,7 +793,7 @@ public void testTrashEmptierCleanDirNotInCheckpointDir() throws Exception {
 
     // Start Emptier in background.
     Runnable emptier = trash.getEmptier();
-    Thread emptierThread = new Thread(emptier);
+    Thread emptierThread = new SubjectInheritingThread(emptier);
     emptierThread.start();
 
     FsShell shell = new FsShell();
@@ -1049,7 +1050,7 @@ private void verifyAuditableTrashEmptier(Trash trash,
     Thread emptierThread = null;
     try {
       Runnable emptier = trash.getEmptier();
-      emptierThread = new Thread(emptier);
+      emptierThread = new SubjectInheritingThread(emptier);
       emptierThread.start();
 
       // Shutdown the emptier thread after a given time
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/loadGenerator/LoadGenerator.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/loadGenerator/LoadGenerator.java
index d52abbc2a99..75a28b59767 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/loadGenerator/LoadGenerator.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/loadGenerator/LoadGenerator.java
@@ -46,6 +46,7 @@
 import org.apache.hadoop.util.Time;
 import org.apache.hadoop.util.Tool;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.util.Preconditions;
 
 import org.slf4j.Logger;
@@ -215,7 +216,7 @@ public LoadGenerator(Configuration conf) throws IOException, UnknownHostExceptio
    * A thread runs for the specified elapsed time if the time isn't zero.
    * Otherwise, it runs forever.
    */
-  private class DFSClientThread extends Thread {
+  private class DFSClientThread extends SubjectInheritingThread {
     private int id;
     private long [] executionTime = new long[TOTAL_OP_TYPES];
     private long [] totalNumOfOps = new long[TOTAL_OP_TYPES];
@@ -230,7 +231,7 @@ private DFSClientThread(int id) {
      * Each iteration decides what's the next operation and then pauses.
      */
     @Override
-    public void run() {
+    public void work() {
       try {
         while (shouldRun) {
           nextOp();
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/TestMD5Hash.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/TestMD5Hash.java
index 99a8fc71898..40d75c38d4d 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/TestMD5Hash.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/TestMD5Hash.java
@@ -18,6 +18,7 @@
 
 package org.apache.hadoop.io;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -93,9 +94,9 @@ public void testMD5Hash() throws Exception {
     assertTrue(closeHash1.hashCode() != closeHash2.hashCode(),
         "hash collision");
      
-    Thread t1 = new Thread() {      
+    SubjectInheritingThread t1 = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         for (int i = 0; i < 100; i++) {
           MD5Hash hash = new MD5Hash(DFF);
           assertEquals(hash, md5HashFF);
@@ -103,9 +104,9 @@ public void run() {
       }
     };
     
-    Thread t2 = new Thread() {
+    SubjectInheritingThread t2 = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         for (int i = 0; i < 100; i++) {
           MD5Hash hash = new MD5Hash(D00);
           assertEquals(hash, md5Hash00);
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/TestText.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/TestText.java
index 95c4abe5e39..7ca61a6358c 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/TestText.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/TestText.java
@@ -27,6 +27,7 @@
 
 import org.apache.hadoop.constants.ConfigConstants;
 import org.apache.hadoop.thirdparty.com.google.common.primitives.Bytes;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -300,13 +301,13 @@ public void testTextText() throws CharacterCodingException {
     assertEquals(8, a.copyBytes().length);
   }
   
-  private class ConcurrentEncodeDecodeThread extends Thread {
+  private class ConcurrentEncodeDecodeThread extends SubjectInheritingThread {
     public ConcurrentEncodeDecodeThread(String name) {
       super(name);
     }
 
     @Override
-    public void run() {
+    public void work() {
       final String name = this.getName();
       DataOutputBuffer out = new DataOutputBuffer();
       DataInputBuffer in = new DataInputBuffer();
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/nativeio/TestNativeIO.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/nativeio/TestNativeIO.java
index 7d4f24efd52..9bccb52a604 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/nativeio/TestNativeIO.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/nativeio/TestNativeIO.java
@@ -56,6 +56,8 @@
 import org.apache.hadoop.test.StatUtils;
 import org.apache.hadoop.util.NativeCodeLoader;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
+
 import static org.apache.hadoop.io.nativeio.NativeIO.POSIX.*;
 import static org.apache.hadoop.io.nativeio.NativeIO.POSIX.Stat.*;
 import static org.apache.hadoop.test.PlatformAssumptions.assumeNotWindows;
@@ -135,9 +137,9 @@ public void testMultiThreadedFstat() throws Exception {
       new AtomicReference<Throwable>();
     List<Thread> statters = new ArrayList<Thread>();
     for (int i = 0; i < 10; i++) {
-      Thread statter = new Thread() {
+      SubjectInheritingThread statter = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           long et = Time.now() + 5000;
           while (Time.now() < et) {
             try {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/nativeio/TestNativeIoInit.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/nativeio/TestNativeIoInit.java
index d44727b4b65..badbbfa23d3 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/nativeio/TestNativeIoInit.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/nativeio/TestNativeIoInit.java
@@ -22,6 +22,7 @@
 import java.io.IOException;
 
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
@@ -44,15 +45,15 @@ public class TestNativeIoInit {
   @Test
   @Timeout(value = 10)
   public void testDeadlockLinux() throws Exception {
-    Thread one = new Thread() {
+    Thread one = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         NativeIO.isAvailable();
       }
     };
-    Thread two = new Thread() {
+    Thread two = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         NativeIO.POSIX.isAvailable();
       }
     };
@@ -66,15 +67,15 @@ public void run() {
   @Timeout(value = 10)
   public void testDeadlockWindows() throws Exception {
     assumeTrue(Path.WINDOWS, "Expected windows");
-    Thread one = new Thread() {
+    SubjectInheritingThread one = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         NativeIO.isAvailable();
       }
     };
-    Thread two = new Thread() {
+    SubjectInheritingThread two = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           NativeIO.Windows.extendWorkingSetSize(100);
         } catch (IOException e) {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/retry/TestFailoverProxy.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/retry/TestFailoverProxy.java
index a541ea99fcf..2533f0944dd 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/retry/TestFailoverProxy.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/io/retry/TestFailoverProxy.java
@@ -28,6 +28,7 @@
 import org.apache.hadoop.io.retry.UnreliableInterface.UnreliableException;
 import org.apache.hadoop.ipc.StandbyException;
 import org.apache.hadoop.util.ThreadUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 
 public class TestFailoverProxy {
@@ -252,7 +253,7 @@ public String failsIfIdentifierDoesntMatch(String identifier)
     
   }
   
-  private static class ConcurrentMethodThread extends Thread {
+  private static class ConcurrentMethodThread extends SubjectInheritingThread {
     
     private UnreliableInterface unreliable;
     public String result;
@@ -262,7 +263,7 @@ public ConcurrentMethodThread(UnreliableInterface unreliable) {
     }
     
     @Override
-    public void run() {
+    public void work() {
       try {
         result = unreliable.failsIfIdentifierDoesntMatch("impl2");
       } catch (Exception e) {
@@ -327,9 +328,9 @@ public void testFailoverBetweenMultipleStandbys()
           RetryPolicies.failoverOnNetworkException(
               RetryPolicies.TRY_ONCE_THEN_FAIL, 10, 1000, 10000));
     
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         ThreadUtil.sleepAtLeastIgnoreInterrupts(millisToSleep);
         impl1.setIdentifier("renamed-impl1");
       }
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestAsyncIPC.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestAsyncIPC.java
index 1e0afe587ca..fe98abce977 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestAsyncIPC.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestAsyncIPC.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
 import org.apache.hadoop.util.concurrent.AsyncGetFuture;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -73,7 +74,7 @@ public void setupConf() {
     Client.setAsynchronousMode(true);
   }
 
-  static class AsyncCaller extends Thread {
+  static class AsyncCaller extends SubjectInheritingThread {
     private Client client;
     private InetSocketAddress server;
     private int count;
@@ -96,7 +97,7 @@ static class AsyncCaller extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       // In case Thread#Start is called, which will spawn new thread.
       Client.setAsynchronousMode(true);
       for (int i = 0; i < count; i++) {
@@ -154,7 +155,7 @@ void assertReturnValues(long timeout, TimeUnit unit)
    * For testing the asynchronous calls of the RPC client
    * implemented with CompletableFuture.
    */
-  static class AsyncCompletableFutureCaller extends Thread {
+  static class AsyncCompletableFutureCaller extends SubjectInheritingThread {
     private final Client client;
     private final InetSocketAddress server;
     private final int count;
@@ -171,7 +172,7 @@ static class AsyncCompletableFutureCaller extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       // Set the RPC client to use asynchronous mode.
       Client.setAsynchronousMode(true);
       long startTime = Time.monotonicNow();
@@ -204,7 +205,7 @@ public void assertReturnValues()
     }
   }
 
-  static class AsyncLimitlCaller extends Thread {
+  static class AsyncLimitlCaller extends SubjectInheritingThread {
     private Client client;
     private InetSocketAddress server;
     private int count;
@@ -242,7 +243,7 @@ public AsyncLimitlCaller(int callerId, Client client, InetSocketAddress server,
     }
 
     @Override
-    public void run() {
+    public void work() {
       // in case Thread#Start is called, which will spawn new thread
       Client.setAsynchronousMode(true);
       for (int i = 0; i < count; i++) {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestCallQueueManager.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestCallQueueManager.java
index bc607d762a3..044c60fe074 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestCallQueueManager.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestCallQueueManager.java
@@ -40,6 +40,7 @@
 import org.apache.hadoop.fs.CommonConfigurationKeys;
 import org.apache.hadoop.ipc.CallQueueManager.CallQueueOverflowException;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
@@ -151,7 +152,7 @@ public void assertCanTake(CallQueueManager<FakeCall> cq, int numberOfTakes,
     int takeAttempts) throws InterruptedException {
 
     Taker taker = new Taker(cq, takeAttempts, -1);
-    Thread t = new Thread(taker);
+    Thread t = new SubjectInheritingThread(taker);
     t.start();
     t.join(100);
 
@@ -164,7 +165,7 @@ public void assertCanPut(CallQueueManager<FakeCall> cq, int numberOfPuts,
     int putAttempts) throws InterruptedException {
 
     Putter putter = new Putter(cq, putAttempts, -1);
-    Thread t = new Thread(putter);
+    Thread t = new SubjectInheritingThread(putter);
     t.start();
     t.join(100);
 
@@ -277,7 +278,7 @@ public void testSwapUnderContention() throws InterruptedException {
     // Create putters and takers
     for (int i=0; i < 1000; i++) {
       Putter p = new Putter(manager, -1, -1);
-      Thread pt = new Thread(p);
+      Thread pt = new SubjectInheritingThread(p);
       producers.add(p);
       threads.put(p, pt);
 
@@ -286,7 +287,7 @@ public void testSwapUnderContention() throws InterruptedException {
 
     for (int i=0; i < 100; i++) {
       Taker t = new Taker(manager, -1, -1);
-      Thread tt = new Thread(t);
+      Thread tt = new SubjectInheritingThread(t);
       consumers.add(t);
       threads.put(t, tt);
 
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestFairCallQueue.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestFairCallQueue.java
index 1afc88c562c..107a9f8587b 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestFairCallQueue.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestFairCallQueue.java
@@ -50,6 +50,7 @@
 import java.util.List;
 import java.util.concurrent.BlockingQueue;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.mockito.Mockito;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.ipc.CallQueueManager.CallQueueOverflowException;
@@ -684,7 +685,7 @@ public void assertCanTake(BlockingQueue<Schedulable> cq, int numberOfTakes,
 
     CountDownLatch latch = new CountDownLatch(numberOfTakes);
     Taker taker = new Taker(cq, takeAttempts, "default", latch);
-    Thread t = new Thread(taker);
+    Thread t = new SubjectInheritingThread(taker);
     t.start();
     latch.await();
 
@@ -698,7 +699,7 @@ public void assertCanPut(BlockingQueue<Schedulable> cq, int numberOfPuts,
 
     CountDownLatch latch = new CountDownLatch(numberOfPuts);
     Putter putter = new Putter(cq, putAttempts, null, latch);
-    Thread t = new Thread(putter);
+    Thread t = new SubjectInheritingThread(putter);
     t.start();
     latch.await();
 
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestIPC.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestIPC.java
index a191095b445..b9a45fe28b8 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestIPC.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestIPC.java
@@ -103,6 +103,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.test.LambdaTestUtils;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -252,7 +253,7 @@ public Writable call(RPC.RpcKind rpcKind, String protocol, Writable param,
     }
   }
 
-  private static class SerialCaller extends Thread {
+  private static class SerialCaller extends SubjectInheritingThread {
     private Client client;
     private InetSocketAddress server;
     private int count;
@@ -265,7 +266,7 @@ public SerialCaller(Client client, InetSocketAddress server, int count) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       for (int i = 0; i < count; i++) {
         try {
           final long param = RANDOM.nextLong();
@@ -996,7 +997,7 @@ private void checkBlocking(int readers, int readerQ, int callQ) throws Exception
     // instantiate the threads, will start in batches
     Thread[] threads = new Thread[clients];
     for (int i=0; i<clients; i++) {
-      threads[i] = new Thread(new Runnable() {
+      threads[i] = new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           Client client = new Client(LongWritable.class, conf);
@@ -1129,7 +1130,7 @@ public void run() {
       final Configuration clientConf = new Configuration();
       clientConf.setInt(CommonConfigurationKeysPublic.IPC_CLIENT_CONNECTION_MAXIDLETIME_KEY, 10000);
       for (int i=0; i < clients; i++) {
-        threads[i] = new Thread(new Runnable(){
+        threads[i] = new SubjectInheritingThread(new Runnable(){
           @Override
           public void run() {
             Client client = new Client(LongWritable.class, clientConf);
@@ -1607,7 +1608,7 @@ public void run() {
   public void testMaxConnections() throws Exception {
     conf.setInt("ipc.server.max.connections", 6);
     Server server = null;
-    Thread connectors[] = new Thread[10];
+    SubjectInheritingThread connectors[] = new SubjectInheritingThread[10];
 
     try {
       server = new TestServer(3, false);
@@ -1616,9 +1617,9 @@ public void testMaxConnections() throws Exception {
       assertEquals(0, server.getNumOpenConnections());
 
       for (int i = 0; i < 10; i++) {
-        connectors[i] = new Thread() {
+        connectors[i] = new SubjectInheritingThread() {
           @Override
-          public void run() {
+          public void work() {
             Socket sock = null;
             try {
               sock = NetUtils.getDefaultSocketFactory(conf).createSocket();
@@ -1687,7 +1688,7 @@ public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
     final AtomicBoolean callStarted = new AtomicBoolean(false);
 
     // Call a random function asynchronously so that we can call stop()
-    new Thread(new Runnable() {
+    new SubjectInheritingThread(new Runnable() {
       public void run() {
         try {
           callStarted.set(true);
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestIPCServerResponder.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestIPCServerResponder.java
index fa9a1606539..032990ba7ba 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestIPCServerResponder.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestIPCServerResponder.java
@@ -44,6 +44,7 @@
 import org.apache.hadoop.ipc.RPC.RpcKind;
 import org.apache.hadoop.ipc.Server.Call;
 import org.apache.hadoop.net.NetUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.slf4j.Logger;
@@ -104,7 +105,7 @@ public Writable call(RPC.RpcKind rpcKind, String protocol, Writable param,
     }
   }
 
-  private static class Caller extends Thread {
+  private static class Caller extends SubjectInheritingThread {
 
     private Client client;
     private int count;
@@ -119,7 +120,7 @@ public Caller(final Client client, final InetSocketAddress address,
     }
 
     @Override
-    public void run() {
+    public void work() {
       for (int i = 0; i < count; i++) {
         try {
           int byteSize = RANDOM.nextInt(BYTE_COUNT);
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestProtoBufRpcServerHandoff.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestProtoBufRpcServerHandoff.java
index fcaecee6561..9c3d87b52aa 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestProtoBufRpcServerHandoff.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestProtoBufRpcServerHandoff.java
@@ -31,6 +31,7 @@
 import org.apache.hadoop.thirdparty.protobuf.BlockingService;
 import org.apache.hadoop.thirdparty.protobuf.RpcController;
 import org.apache.hadoop.thirdparty.protobuf.ServiceException;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.ipc.protobuf.TestProtos;
 import org.apache.hadoop.ipc.protobuf.TestRpcServiceProtos.TestProtobufRpcHandoffProto;
@@ -219,9 +220,9 @@ public static class TestProtoBufRpcServerHandoffServer
       final ProtobufRpcEngineCallback2 callback =
           ProtobufRpcEngine2.Server.registerForDeferredResponse2();
       final long sleepTime = request.getSleepTime();
-      new Thread() {
+      new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           try {
             Thread.sleep(sleepTime);
           } catch (InterruptedException e) {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRPC.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRPC.java
index 4d650161a4a..6cb4697ddaa 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRPC.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRPC.java
@@ -24,6 +24,7 @@
 
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
 import org.apache.hadoop.thirdparty.protobuf.ServiceException;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.HadoopIllegalArgumentException;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.CommonConfigurationKeys;
@@ -965,7 +966,7 @@ public void testRPCInterrupted() throws Exception {
       for (int i = 0; i < numConcurrentRPC; i++) {
         final int num = i;
         final TestRpcService proxy = getClient(addr, conf);
-        Thread rpcThread = new Thread(new Runnable() {
+        Thread rpcThread = new SubjectInheritingThread(new Runnable() {
           @Override
           public void run() {
             try {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRPCWaitForProxy.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRPCWaitForProxy.java
index 30e5ed54953..acd5735e08c 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRPCWaitForProxy.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRPCWaitForProxy.java
@@ -18,6 +18,7 @@
 package org.apache.hadoop.ipc;
 
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -108,7 +109,7 @@ public void testInterruptedWaitForProxy() throws Throwable {
    * throwable that was raised in the process
    */
 
-  private class RpcThread extends Thread {
+  private class RpcThread extends SubjectInheritingThread {
     private Throwable caught;
     private int connectRetries;
     private volatile boolean waitStarted = false;
@@ -117,7 +118,7 @@ private RpcThread(int connectRetries) {
       this.connectRetries = connectRetries;
     }
     @Override
-    public void run() {
+    public void work() {
       try {
         Configuration config = new Configuration(conf);
         config.setInt(IPC_CLIENT_CONNECT_MAX_RETRIES_KEY,
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRpcServerHandoff.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRpcServerHandoff.java
index f30a6165edc..828ceefe07c 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRpcServerHandoff.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestRpcServerHandoff.java
@@ -34,6 +34,7 @@
 import org.apache.hadoop.io.BytesWritable;
 import org.apache.hadoop.io.Writable;
 import org.apache.hadoop.net.NetUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.slf4j.Logger;
@@ -116,7 +117,7 @@ public void testDeferredResponse() throws IOException, InterruptedException,
           new ClientCallable(serverAddress, conf, requestBytes);
 
       FutureTask<Writable> future = new FutureTask<Writable>(clientCallable);
-      Thread clientThread = new Thread(future);
+      Thread clientThread = new SubjectInheritingThread(future);
       clientThread.start();
 
       server.awaitInvocation();
@@ -146,7 +147,7 @@ public void testDeferredException() throws IOException, InterruptedException,
           new ClientCallable(serverAddress, conf, requestBytes);
 
       FutureTask<Writable> future = new FutureTask<Writable>(clientCallable);
-      Thread clientThread = new Thread(future);
+      Thread clientThread = new SubjectInheritingThread(future);
       clientThread.start();
 
       server.awaitInvocation();
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestSocketFactory.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestSocketFactory.java
index 51c66abb3fc..50202214fe5 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestSocketFactory.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/ipc/TestSocketFactory.java
@@ -38,6 +38,7 @@
 import org.apache.hadoop.net.NetUtils;
 import org.apache.hadoop.net.SocksSocketFactory;
 import org.apache.hadoop.net.StandardSocketFactory;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -63,7 +64,7 @@ public class TestSocketFactory {
   private void startTestServer() throws Exception {
     // start simple tcp server.
     serverRunnable = new ServerRunnable();
-    serverThread = new Thread(serverRunnable);
+    serverThread = new SubjectInheritingThread(serverRunnable);
     serverThread.start();
     final long timeout = System.currentTimeMillis() + START_STOP_TIMEOUT_SEC * 1000;
     while (!serverRunnable.isReady()) {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/impl/TestSinkQueue.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/impl/TestSinkQueue.java
index 33f9946e94d..69a732686df 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/impl/TestSinkQueue.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/impl/TestSinkQueue.java
@@ -21,6 +21,7 @@
 import java.util.ConcurrentModificationException;
 import java.util.concurrent.CountDownLatch;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -79,8 +80,8 @@ private void testEmptyBlocking(int awhile) throws Exception {
     final SinkQueue<Integer> q = new SinkQueue<Integer>(2);
     final Runnable trigger = mock(Runnable.class);
     // try consuming emtpy equeue and blocking
-    Thread t = new Thread() {
-      @Override public void run() {
+    SubjectInheritingThread t = new SubjectInheritingThread() {
+      @Override public void work() {
         try {
           assertEquals(1, (int) q.dequeue(), "element");
           q.consume(new Consumer<Integer>() {
@@ -255,8 +256,8 @@ private SinkQueue<Integer> newSleepingConsumerQueue(int capacity,
       q.enqueue(i);
     }
     final CountDownLatch barrier = new CountDownLatch(1);
-    Thread t = new Thread() {
-      @Override public void run() {
+    SubjectInheritingThread t = new SubjectInheritingThread() {
+      @Override public void work() {
         try {
           Thread.sleep(10); // causes failure without barrier
           q.consume(new Consumer<Integer>() {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/lib/TestMutableMetrics.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/lib/TestMutableMetrics.java
index f423b57d1c3..0e5df58ded5 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/lib/TestMutableMetrics.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/lib/TestMutableMetrics.java
@@ -40,6 +40,7 @@
 import org.apache.hadoop.metrics2.MetricsRecordBuilder;
 import org.apache.hadoop.metrics2.util.Quantile;
 import org.apache.hadoop.thirdparty.com.google.common.math.Stats;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.slf4j.Logger;
@@ -209,7 +210,7 @@ interface TestProtocol {
       rates.add("metric" + i, 0);
     }
 
-    Thread[] threads = new Thread[n];
+    SubjectInheritingThread[] threads = new SubjectInheritingThread[n];
     final CountDownLatch firstAddsFinished = new CountDownLatch(threads.length);
     final CountDownLatch firstSnapshotsFinished = new CountDownLatch(1);
     final CountDownLatch secondAddsFinished =
@@ -220,9 +221,9 @@ interface TestProtocol {
     final Random sleepRandom = new Random(seed);
     for (int tIdx = 0; tIdx < threads.length; tIdx++) {
       final int threadIdx = tIdx;
-      threads[threadIdx] = new Thread() {
+      threads[threadIdx] = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           try {
             for (int i = 0; i < 1000; i++) {
               rates.add("metric" + (i % n), (i / n) % 2 == 0 ? 1 : 2);
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/source/TestJvmMetrics.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/source/TestJvmMetrics.java
index 2110f33981d..7429e7e525a 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/source/TestJvmMetrics.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/source/TestJvmMetrics.java
@@ -39,6 +39,7 @@
 import org.apache.hadoop.service.ServiceStateException;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.JvmPauseMonitor;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Timeout;
 
 import java.util.ArrayList;
@@ -296,11 +297,11 @@ private static void updateThreadsAndWait(List<TestThread> threads,
     }
   }
 
-  static class TestThread extends Thread {
+  static class TestThread extends SubjectInheritingThread {
     private volatile boolean exit = false;
     private boolean exited = false;
     @Override
-    public void run() {
+    public void work() {
       while (!exit) {
         try {
           Thread.sleep(1000);
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/unix/TestDomainSocket.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/unix/TestDomainSocket.java
index a1704a0ec30..f5f253eec89 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/unix/TestDomainSocket.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/unix/TestDomainSocket.java
@@ -47,6 +47,7 @@
 import org.apache.hadoop.net.unix.DomainSocket.DomainChannel;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Shell;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.apache.hadoop.thirdparty.com.google.common.io.Files;
 
@@ -457,8 +458,8 @@ void testClientServer1(final Class<? extends WriteStrategy> writeStrategyClass,
         new ArrayBlockingQueue<Throwable>(2);
     final DomainSocket serv = (preConnectedSockets != null) ?
       null : DomainSocket.bindAndListen(TEST_PATH);
-    Thread serverThread = new Thread() {
-      public void run(){
+    Thread serverThread = new SubjectInheritingThread() {
+      public void work(){
         // Run server
         DomainSocket conn = null;
         try {
@@ -485,8 +486,8 @@ public void run(){
     };
     serverThread.start();
     
-    Thread clientThread = new Thread() {
-      public void run(){
+    SubjectInheritingThread clientThread = new SubjectInheritingThread() {
+      public void work(){
         try {
           DomainSocket client = preConnectedSockets != null ?
                 preConnectedSockets[1] : DomainSocket.connect(TEST_PATH);
@@ -626,8 +627,8 @@ public void testFdPassing() throws Exception {
     for (int i = 0; i < passedFiles.length; i++) {
       passedFds[i] = passedFiles[i].getInputStream().getFD();
     }
-    Thread serverThread = new Thread() {
-      public void run(){
+    Thread serverThread = new SubjectInheritingThread() {
+      public void work(){
         // Run server
         DomainSocket conn = null;
         try {
@@ -649,8 +650,8 @@ public void run(){
     };
     serverThread.start();
 
-    Thread clientThread = new Thread() {
-      public void run(){
+    Thread clientThread = new SubjectInheritingThread() {
+      public void work(){
         try {
           DomainSocket client = DomainSocket.connect(TEST_PATH);
           OutputStream clientOutputStream = client.getOutputStream();
@@ -783,7 +784,7 @@ public void run() {
         }
       }
     };
-    Thread readerThread = new Thread(reader);
+    Thread readerThread = new SubjectInheritingThread(reader);
     readerThread.start();
     socks[0].getOutputStream().write(1);
     socks[0].getOutputStream().write(2);
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/unix/TestDomainSocketWatcher.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/unix/TestDomainSocketWatcher.java
index f78005a6ed3..58fe3f44ecf 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/unix/TestDomainSocketWatcher.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/net/unix/TestDomainSocketWatcher.java
@@ -32,6 +32,7 @@
 import org.junit.jupiter.api.Timeout;
 
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.Uninterruptibles;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -128,7 +129,7 @@ public void testStress() throws Exception {
     final ArrayList<DomainSocket[]> pairs = new ArrayList<DomainSocket[]>();
     final AtomicInteger handled = new AtomicInteger(0);
 
-    final Thread adderThread = new Thread(new Runnable() {
+    final Thread adderThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
@@ -155,7 +156,7 @@ public boolean handle(DomainSocket sock) {
       }
     });
     
-    final Thread removerThread = new Thread(new Runnable() {
+    final Thread removerThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         final Random random = new Random();
@@ -199,7 +200,7 @@ public void testStressInterruption() throws Exception {
     final ArrayList<DomainSocket[]> pairs = new ArrayList<DomainSocket[]>();
     final AtomicInteger handled = new AtomicInteger(0);
 
-    final Thread adderThread = new Thread(new Runnable() {
+    final Thread adderThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
@@ -227,7 +228,7 @@ public boolean handle(DomainSocket sock) {
       }
     });
 
-    final Thread removerThread = new Thread(new Runnable() {
+    final Thread removerThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         final Random random = new Random();
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestAuthorizationContext.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestAuthorizationContext.java
index fe6bc4f58de..136e28b56d4 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestAuthorizationContext.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestAuthorizationContext.java
@@ -17,6 +17,7 @@
  */
 package org.apache.hadoop.security;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -42,7 +43,7 @@ public void testClearAuthorizationHeader() {
   public void testThreadLocalIsolation() throws Exception {
     byte[] mainHeader = "main-thread".getBytes();
     AuthorizationContext.setCurrentAuthorizationHeader(mainHeader);
-    Thread t = new Thread(() -> {
+    SubjectInheritingThread t = new SubjectInheritingThread(() -> {
       Assertions.assertNull(AuthorizationContext.getCurrentAuthorizationHeader());
       byte[] threadHeader = "other-thread".getBytes();
       AuthorizationContext.setCurrentAuthorizationHeader(threadHeader);
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestGroupsCaching.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestGroupsCaching.java
index 5a2927f71c1..74f00f9d8e9 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestGroupsCaching.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestGroupsCaching.java
@@ -31,6 +31,7 @@
 
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.FakeTimer;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -406,10 +407,10 @@ public void testOnlyOneRequestWhenNoEntryIsCached() throws Exception {
     FakeGroupMapping.clearBlackList();
     FakeGroupMapping.setGetGroupsDelayMs(100);
 
-    ArrayList<Thread> threads = new ArrayList<Thread>();
+    ArrayList<SubjectInheritingThread> threads = new ArrayList<SubjectInheritingThread>();
     for (int i = 0; i < 10; i++) {
-      threads.add(new Thread() {
-        public void run() {
+      threads.add(new SubjectInheritingThread() {
+        public void work() {
           try {
             assertEquals(2, groups.getGroups("me").size());
           } catch (IOException e) {
@@ -451,10 +452,10 @@ public void testOnlyOneRequestWhenExpiredEntryExists() throws Exception {
     timer.advance(400 * 1000);
     Thread.sleep(100);
 
-    ArrayList<Thread> threads = new ArrayList<Thread>();
+    ArrayList<SubjectInheritingThread> threads = new ArrayList<SubjectInheritingThread>();
     for (int i = 0; i < 10; i++) {
-      threads.add(new Thread() {
-        public void run() {
+      threads.add(new SubjectInheritingThread() {
+        public void work() {
           try {
             assertEquals(2, groups.getGroups("me").size());
           } catch (IOException e) {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMapping.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMapping.java
index 1bb43ffcd5e..9d0661af3e0 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMapping.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMapping.java
@@ -59,6 +59,7 @@
 import org.apache.hadoop.security.alias.CredentialProviderFactory;
 import org.apache.hadoop.security.alias.JavaKeyStoreProvider;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -414,7 +415,7 @@ public void testLdapConnectionTimeout()
       // Below we create a LDAP server which will accept a client request;
       // but it will never reply to the bind (connect) request.
       // Client of this LDAP server is expected to get a connection timeout.
-      final Thread ldapServer = new Thread(new Runnable() {
+      final Thread ldapServer = new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           try {
@@ -469,7 +470,7 @@ public void testLdapReadTimeout() throws IOException, InterruptedException {
       // authenticate it successfully; but it will never reply to the following
       // query request.
       // Client of this LDAP server is expected to get a read timeout.
-      final Thread ldapServer = new Thread(new Runnable() {
+      final Thread ldapServer = new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           try {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestUserGroupInformation.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestUserGroupInformation.java
index 2fb5b6c22eb..626021b18c6 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestUserGroupInformation.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestUserGroupInformation.java
@@ -33,6 +33,7 @@
 import org.apache.hadoop.util.Shell;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.BeforeAll;
@@ -1023,12 +1024,12 @@ public Void run() throws Exception {
       }});
   }
 
-  static class GetTokenThread extends Thread {
+  static class GetTokenThread extends SubjectInheritingThread {
     boolean runThread = true;
     volatile ConcurrentModificationException cme = null;
 
     @Override
-    public void run() {
+    public void work() {
       while(runThread) {
         try {
           UserGroupInformation.getCurrentUser().getCredentials();
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/TestServiceLifecycle.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/TestServiceLifecycle.java
index ccbc0a009fb..67f6902e301 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/TestServiceLifecycle.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/TestServiceLifecycle.java
@@ -25,6 +25,7 @@
 import org.apache.hadoop.service.Service;
 import org.apache.hadoop.service.ServiceStateChangeListener;
 import org.apache.hadoop.service.ServiceStateException;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -404,7 +405,7 @@ private AsyncSelfTerminatingService(int timeout) {
 
     @Override
     protected void serviceStart() throws Exception {
-      new Thread(this).start();
+      new SubjectInheritingThread(this).start();
       super.serviceStart();
     }
 
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/launcher/testservices/RunningService.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/launcher/testservices/RunningService.java
index 3093aa1ff5d..6f917467f2b 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/launcher/testservices/RunningService.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/service/launcher/testservices/RunningService.java
@@ -20,6 +20,7 @@
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.service.AbstractService;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -58,7 +59,7 @@ protected void serviceInit(Configuration conf) throws Exception {
 
   @Override
   protected void serviceStart() throws Exception {
-    Thread thread = new Thread(this);
+    Thread thread = new SubjectInheritingThread(this);
     thread.setName(getName());
     thread.start();
   }
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/MultithreadedTestUtil.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/MultithreadedTestUtil.java
index e270ee68000..a8dbd395b82 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/MultithreadedTestUtil.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/MultithreadedTestUtil.java
@@ -21,6 +21,7 @@
 import java.util.Set;
 
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -175,7 +176,7 @@ public Iterable<? extends Thread> getTestThreads() {
    * A thread that can be added to a test context, and properly
    * passes exceptions through.
    */
-  public static abstract class TestingThread extends Thread {
+  public static abstract class TestingThread extends SubjectInheritingThread {
     protected final TestContext ctx;
     protected boolean stopped;
 
@@ -184,7 +185,7 @@ public TestingThread(TestContext ctx) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         doWork();
       } catch (Throwable t) {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestTimedOutTestsListener.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestTimedOutTestsListener.java
index 6805dcd2fd4..1b6ddcc263a 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestTimedOutTestsListener.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/test/TestTimedOutTestsListener.java
@@ -23,6 +23,7 @@
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
@@ -59,7 +60,7 @@ public Deadlock() {
       }
     }
   
-    class DeadlockThread extends Thread {
+    class DeadlockThread extends SubjectInheritingThread {
       private Lock lock1 = null;
   
       private Lock lock2 = null;
@@ -84,7 +85,7 @@ class DeadlockThread extends Thread {
         this.useSync = false;
       }
   
-      public void run() {
+      public void work() {
         if (useSync) {
           syncLock();
         } else {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestAutoCloseableLock.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestAutoCloseableLock.java
index 90beb58aee4..6080c1b37e8 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestAutoCloseableLock.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestAutoCloseableLock.java
@@ -21,6 +21,8 @@
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
+
 import org.junit.jupiter.api.Test;
 /**
  * A test class for AutoCloseableLock.
@@ -54,9 +56,9 @@ public void testMultipleThread() throws Exception {
     AutoCloseableLock lock = new AutoCloseableLock();
     lock.acquire();
     assertTrue(lock.isLocked());
-    Thread competingThread = new Thread() {
+    SubjectInheritingThread competingThread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         assertTrue(lock.isLocked());
         assertFalse(lock.tryLock());
       }
@@ -79,9 +81,9 @@ public void testTryWithResourceSyntax() throws Exception {
     try(AutoCloseableLock localLock = lock.acquire()) {
       assertEquals(localLock, lock);
       assertTrue(lock.isLocked());
-      Thread competingThread = new Thread() {
+      SubjectInheritingThread competingThread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           assertTrue(lock.isLocked());
           assertFalse(lock.tryLock());
         }
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestInstrumentedLock.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestInstrumentedLock.java
index fb9e773c7d0..0ff2bdeafc3 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestInstrumentedLock.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestInstrumentedLock.java
@@ -26,6 +26,7 @@
 import org.junit.jupiter.api.TestInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -52,9 +53,9 @@ public void testMultipleThread(TestInfo testInfo) throws Exception {
     InstrumentedLock lock = new InstrumentedLock(testname, LOG, 0, 300);
     lock.lock();
     try {
-      Thread competingThread = new Thread() {
+      SubjectInheritingThread competingThread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           assertFalse(lock.tryLock());
         }
       };
@@ -89,9 +90,9 @@ public void unlock() {
     AutoCloseableLock acl = new AutoCloseableLock(lock);
     try (AutoCloseable localLock = acl.acquire()) {
       assertEquals(acl, localLock);
-      Thread competingThread = new Thread() {
+      SubjectInheritingThread competingThread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           assertNotEquals(Thread.currentThread(), lockThread.get());
           assertFalse(lock.tryLock());
         }
@@ -253,7 +254,7 @@ void logWaitWarning(long lockHeldTime, SuppressedSnapshot stats) {
 
   private Thread lockUnlockThread(Lock lock) throws InterruptedException {
     CountDownLatch countDownLatch = new CountDownLatch(1);
-    Thread t = new Thread(() -> {
+    Thread t = new SubjectInheritingThread(() -> {
       try {
         assertFalse(lock.tryLock());
         countDownLatch.countDown();
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestInstrumentedReadWriteLock.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestInstrumentedReadWriteLock.java
index 6bb5d08e154..aaf75234b0e 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestInstrumentedReadWriteLock.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestInstrumentedReadWriteLock.java
@@ -24,6 +24,7 @@
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInfo;
 import org.junit.jupiter.api.Timeout;
@@ -68,17 +69,17 @@ public void release() {
     final AutoCloseableLock readLock = new AutoCloseableLock(
         readWriteLock.readLock());
     try (AutoCloseableLock lock = writeLock.acquire()) {
-      Thread competingWriteThread = new Thread() {
+      Thread competingWriteThread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           assertFalse(writeLock.tryLock());
         }
       };
       competingWriteThread.start();
       competingWriteThread.join();
-      Thread competingReadThread = new Thread() {
+      Thread competingReadThread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           assertFalse(readLock.tryLock());
         };
       };
@@ -104,18 +105,18 @@ public void testReadLock(TestInfo testInfo) throws Exception {
     final AutoCloseableLock writeLock = new AutoCloseableLock(
         readWriteLock.writeLock());
     try (AutoCloseableLock lock = readLock.acquire()) {
-      Thread competingReadThread = new Thread() {
+      SubjectInheritingThread competingReadThread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           assertTrue(readLock.tryLock());
           readLock.release();
         }
       };
       competingReadThread.start();
       competingReadThread.join();
-      Thread competingWriteThread = new Thread() {
+      SubjectInheritingThread competingWriteThread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           assertFalse(writeLock.tryLock());
         }
       };
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestPureJavaCrc32.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestPureJavaCrc32.java
index cee0fcef092..60ebc02398e 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestPureJavaCrc32.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestPureJavaCrc32.java
@@ -29,6 +29,7 @@
 import java.util.zip.CRC32;
 import java.util.zip.Checksum;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -316,7 +317,7 @@ private static BenchResult doBench(Class<? extends Checksum> clazz,
         final int numThreads, final byte[] bytes, final int size)
             throws Exception {
 
-      final Thread[] threads = new Thread[numThreads];
+      final SubjectInheritingThread[] threads = new SubjectInheritingThread[numThreads];
       final BenchResult[] results = new BenchResult[threads.length];
 
       {
@@ -326,11 +327,11 @@ private static BenchResult doBench(Class<? extends Checksum> clazz,
 
         for(int i = 0; i < threads.length; i++) {
           final int index = i;
-          threads[i] = new Thread() {
+          threads[i] = new SubjectInheritingThread() {
             final Checksum crc = ctor.newInstance();
   
             @Override
-            public void run() {
+            public void work() {
               final long st = System.nanoTime();
               crc.reset();
               for (int i = 0; i < trials; i++) {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestReflectionUtils.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestReflectionUtils.java
index 7d8bece0d67..89cc5eefa86 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestReflectionUtils.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestReflectionUtils.java
@@ -31,6 +31,7 @@
 
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.test.GenericTestUtils.LogCapturer;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -70,11 +71,11 @@ private void doTestCache() {
 
   @Test
   public void testThreadSafe() throws Exception {
-    Thread[] th = new Thread[32];
+    SubjectInheritingThread[] th = new SubjectInheritingThread[32];
     for (int i=0; i<th.length; i++) {
-      th[i] = new Thread() {
+      th[i] = new SubjectInheritingThread() {
           @Override
-          public void run() {
+          public void work() {
             try {
               doTestCache();
             } catch (Throwable t) {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShell.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShell.java
index 1c5dadd7a37..71996dbe123 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShell.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShell.java
@@ -41,6 +41,7 @@
 
 import org.apache.hadoop.fs.FileUtil;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import static org.apache.hadoop.util.Shell.*;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
@@ -477,9 +478,9 @@ public void testDestroyAllShellProcesses() throws Throwable {
     final ShellCommandExecutor shexc1 = new ShellCommandExecutor(shellCmd);
     final ShellCommandExecutor shexc2 = new ShellCommandExecutor(shellCmd);
 
-    Thread shellThread1 = new Thread() {
+    SubjectInheritingThread shellThread1 = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           shexc1.execute();
         } catch(IOException ioe) {
@@ -487,9 +488,9 @@ public void run() {
         }
       }
     };
-    Thread shellThread2 = new Thread() {
+    SubjectInheritingThread shellThread2 = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           shexc2.execute();
         } catch(IOException ioe) {
diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShutdownThreadsHelper.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShutdownThreadsHelper.java
index 6bad38b0e1b..28869ed30b1 100644
--- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShutdownThreadsHelper.java
+++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShutdownThreadsHelper.java
@@ -17,6 +17,7 @@
  */
 package org.apache.hadoop.util;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -39,7 +40,7 @@ public void run() {
   @Test
   @Timeout(value = 3)
   public void testShutdownThread() {
-    Thread thread = new Thread(sampleRunnable);
+    Thread thread = new SubjectInheritingThread(sampleRunnable);
     thread.start();
     boolean ret = ShutdownThreadsHelper.shutdownThread(thread);
     boolean isTerminated = !thread.isAlive();
diff --git a/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/KMSBenchmark.java b/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/KMSBenchmark.java
index 32c6d705008..118e63ca137 100644
--- a/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/KMSBenchmark.java
+++ b/hadoop-common-project/hadoop-kms/src/test/java/org/apache/hadoop/crypto/key/kms/server/KMSBenchmark.java
@@ -28,6 +28,7 @@
 import org.apache.hadoop.util.Time;
 import org.apache.hadoop.util.Tool;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -325,7 +326,7 @@ void printStats() {
   /**
    * One of the threads that perform stats operations.
    */
-  private class StatsDaemon extends Thread {
+  private class StatsDaemon extends SubjectInheritingThread {
     private final int daemonId;
     private int opsPerThread;
     private String arg1;      // argument passed to executeOp()
@@ -341,7 +342,7 @@ private class StatsDaemon extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       localNumOpsExecuted = 0;
       localCumulativeTime = 0;
       arg1 = statsOp.getExecutionArgument(daemonId);
diff --git a/hadoop-common-project/hadoop-registry/src/main/java/org/apache/hadoop/registry/server/dns/RegistryDNS.java b/hadoop-common-project/hadoop-registry/src/main/java/org/apache/hadoop/registry/server/dns/RegistryDNS.java
index e99c49f7dc6..ed6c906cda9 100644
--- a/hadoop-common-project/hadoop-registry/src/main/java/org/apache/hadoop/registry/server/dns/RegistryDNS.java
+++ b/hadoop-common-project/hadoop-registry/src/main/java/org/apache/hadoop/registry/server/dns/RegistryDNS.java
@@ -29,6 +29,7 @@
 import org.apache.hadoop.registry.client.types.yarn.YarnRegistryAttributes;
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.concurrent.HadoopExecutors;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xbill.DNS.CNAMERecord;
@@ -174,7 +175,7 @@ public RegistryDNS(String name) {
 
           @Override
           public Thread newThread(Runnable r) {
-            return new Thread(r,
+            return new SubjectInheritingThread(r,
                 "RegistryDNS "
                     + counter.getAndIncrement());
           }
diff --git a/hadoop-common-project/hadoop-registry/src/main/java/org/apache/hadoop/registry/server/services/RegistryAdminService.java b/hadoop-common-project/hadoop-registry/src/main/java/org/apache/hadoop/registry/server/services/RegistryAdminService.java
index 7fe6f2d0afd..e42edd63815 100644
--- a/hadoop-common-project/hadoop-registry/src/main/java/org/apache/hadoop/registry/server/services/RegistryAdminService.java
+++ b/hadoop-common-project/hadoop-registry/src/main/java/org/apache/hadoop/registry/server/services/RegistryAdminService.java
@@ -37,6 +37,7 @@
 import org.apache.hadoop.registry.client.types.RegistryPathStatus;
 import org.apache.hadoop.registry.client.types.ServiceRecord;
 import org.apache.hadoop.util.concurrent.HadoopExecutors;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.ZooDefs;
 import org.apache.zookeeper.data.ACL;
@@ -115,7 +116,7 @@ public RegistryAdminService(String name,
 
           @Override
           public Thread newThread(Runnable r) {
-            return new Thread(r,
+            return new SubjectInheritingThread(r,
                 "RegistryAdminService " + counter.getAndIncrement());
           }
         });
diff --git a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DeadNodeDetector.java b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DeadNodeDetector.java
index 0dc1b1686b9..6cbff92eab0 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DeadNodeDetector.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DeadNodeDetector.java
@@ -24,6 +24,7 @@
 import org.apache.hadoop.hdfs.protocol.DatanodeLocalInfo;
 import org.apache.hadoop.hdfs.protocol.HdfsConstants;
 import org.apache.hadoop.util.Daemon;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -322,12 +323,12 @@ static void setDisabledProbeThreadForTest(
   @VisibleForTesting
   void startProbeScheduler() {
     probeDeadNodesSchedulerThr =
-            new Thread(new ProbeScheduler(this, ProbeType.CHECK_DEAD));
+            new SubjectInheritingThread(new ProbeScheduler(this, ProbeType.CHECK_DEAD));
     probeDeadNodesSchedulerThr.setDaemon(true);
     probeDeadNodesSchedulerThr.start();
 
     probeSuspectNodesSchedulerThr =
-            new Thread(new ProbeScheduler(this, ProbeType.CHECK_SUSPECT));
+            new SubjectInheritingThread(new ProbeScheduler(this, ProbeType.CHECK_SUSPECT));
     probeSuspectNodesSchedulerThr.setDaemon(true);
     probeSuspectNodesSchedulerThr.start();
   }
diff --git a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/util/CombinedHostsFileReader.java b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/util/CombinedHostsFileReader.java
index 33f4934e548..471b0186cf2 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/util/CombinedHostsFileReader.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/util/CombinedHostsFileReader.java
@@ -42,6 +42,7 @@
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
 import org.apache.hadoop.hdfs.protocol.DatanodeAdminProperties;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -144,7 +145,7 @@ public DatanodeAdminProperties[] call() throws Exception {
         }
       });
 
-    Thread thread = new Thread(futureTask);
+    Thread thread = new SubjectInheritingThread(futureTask);
     thread.start();
 
     try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRequestHedgingProxyProvider.java b/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRequestHedgingProxyProvider.java
index 8c913377c01..31b680774f7 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRequestHedgingProxyProvider.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRequestHedgingProxyProvider.java
@@ -45,6 +45,7 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import static org.apache.hadoop.test.GenericTestUtils.assertExceptionContains;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -677,9 +678,9 @@ public long[] answer(InvocationOnMock invocation) throws Throwable {
     assertEquals(1, stats[0]);
     assertEquals(1, counter.get());
 
-    Thread t = new Thread() {
+    Thread t = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           // Fail over between calling delayProxy.getStats() and throw
           // exception.
diff --git a/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/util/TestByteArrayManager.java b/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/util/TestByteArrayManager.java
index c12b6831d45..f07d67f0c0b 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/util/TestByteArrayManager.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-client/src/test/java/org/apache/hadoop/hdfs/util/TestByteArrayManager.java
@@ -25,6 +25,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Time;
 import org.junit.jupiter.api.Test;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.event.Level;
@@ -226,7 +227,7 @@ static <T> void waitForAll(List<Future<T>> furtures) throws Exception {
     }
   }
 
-  static class AllocatorThread extends Thread {
+  static class AllocatorThread extends SubjectInheritingThread {
     private final ByteArrayManager bam;
     private final int arrayLength;
     private byte[] array;
@@ -237,7 +238,7 @@ static class AllocatorThread extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         array = bam.newByteArray(arrayLength);
       } catch (InterruptedException e) {
@@ -333,9 +334,9 @@ public void testByteArrayManager() throws Exception {
     }
     
     final List<Exception> exceptions = new ArrayList<Exception>();
-    final Thread randomRecycler = new Thread() {
+    final Thread randomRecycler = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         LOG.info("randomRecycler start");
         for(int i = 0; shouldRun(); i++) {
           final int j = ThreadLocalRandom.current().nextInt(runners.length);
@@ -524,7 +525,7 @@ public void run() {
     
     Thread start(int n) {
       this.n = n;
-      final Thread t = new Thread(this);
+      final Thread t = new SubjectInheritingThread(this);
       t.start();
       return t;
     }
diff --git a/hadoop-hdfs-project/hadoop-hdfs-nfs/src/main/java/org/apache/hadoop/hdfs/nfs/nfs3/AsyncDataService.java b/hadoop-hdfs-project/hadoop-hdfs-nfs/src/main/java/org/apache/hadoop/hdfs/nfs/nfs3/AsyncDataService.java
index cbbcccf3ca0..1298252740e 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-nfs/src/main/java/org/apache/hadoop/hdfs/nfs/nfs3/AsyncDataService.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-nfs/src/main/java/org/apache/hadoop/hdfs/nfs/nfs3/AsyncDataService.java
@@ -22,6 +22,7 @@
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -47,7 +48,7 @@ public AsyncDataService() {
     threadFactory = new ThreadFactory() {
       @Override
       public Thread newThread(Runnable r) {
-        return new Thread(threadGroup, r);
+        return new SubjectInheritingThread(threadGroup, r);
       }
     };
 
diff --git a/hadoop-hdfs-project/hadoop-hdfs-nfs/src/test/java/org/apache/hadoop/hdfs/nfs/TestUdpServer.java b/hadoop-hdfs-project/hadoop-hdfs-nfs/src/test/java/org/apache/hadoop/hdfs/nfs/TestUdpServer.java
index 46c16d3c7fa..8a0242678e5 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-nfs/src/test/java/org/apache/hadoop/hdfs/nfs/TestUdpServer.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-nfs/src/test/java/org/apache/hadoop/hdfs/nfs/TestUdpServer.java
@@ -29,6 +29,7 @@
 import org.apache.hadoop.oncrpc.XDR;
 import org.apache.hadoop.oncrpc.security.CredentialsNone;
 import org.apache.hadoop.oncrpc.security.VerifierNone;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 // TODO: convert this to Junit
 public class TestUdpServer {
@@ -68,16 +69,16 @@ public static void main(String[] args) throws InterruptedException {
     //testDump();
   }
   
-  static class Runtest1 extends Thread {
+  static class Runtest1 extends SubjectInheritingThread {
     @Override
-    public void run() {
+    public void work() {
       testGetportMount();
     }
   }
   
-  static class Runtest2 extends Thread {
+  static class Runtest2 extends SubjectInheritingThread {
     @Override
-    public void run() {
+    public void work() {
       testDump();
     }
   }
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/resolver/order/RouterResolver.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/resolver/order/RouterResolver.java
index 91af1ca06ac..aaba9992a25 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/resolver/order/RouterResolver.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/resolver/order/RouterResolver.java
@@ -29,6 +29,7 @@
 import org.apache.hadoop.hdfs.server.federation.router.RouterRpcServer;
 import org.apache.hadoop.hdfs.server.federation.store.MembershipStore;
 import org.apache.hadoop.hdfs.server.federation.store.StateStoreService;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -97,7 +98,7 @@ private synchronized void updateSubclusterMapping() {
     if (subclusterMapping == null
         || (monotonicNow() - lastUpdated) > minUpdateTime) {
       // Fetch the mapping asynchronously
-      Thread updater = new Thread(new Runnable() {
+      Thread updater = new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           final MembershipStore membershipStore = getMembershipStore();
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/ConnectionManager.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/ConnectionManager.java
index 2ffc5f0b5d8..eb2a4660819 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/ConnectionManager.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/ConnectionManager.java
@@ -36,6 +36,7 @@
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.eclipse.jetty.util.ajax.JSON;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -458,7 +459,7 @@ public void run() {
   /**
    * Thread that creates connections asynchronously.
    */
-  static class ConnectionCreator extends Thread {
+  static class ConnectionCreator extends SubjectInheritingThread {
     /** If the creator is running. */
     private boolean running = true;
     /** Queue to push work to. */
@@ -470,7 +471,7 @@ static class ConnectionCreator extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       while (this.running) {
         try {
           ConnectionPool pool = this.queue.take();
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/MountTableRefresherThread.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/MountTableRefresherThread.java
index 40ff843fa1d..a7c1fb6caab 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/MountTableRefresherThread.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/MountTableRefresherThread.java
@@ -25,13 +25,14 @@
 import org.apache.hadoop.hdfs.server.federation.store.protocol.RefreshMountTableEntriesResponse;
 import org.apache.hadoop.security.SecurityUtil;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * Base class for updating mount table cache on all the router.
  */
-public class MountTableRefresherThread extends Thread {
+public class MountTableRefresherThread extends SubjectInheritingThread {
   private static final Logger LOG =
       LoggerFactory.getLogger(MountTableRefresherThread.class);
   private boolean success;
@@ -61,7 +62,7 @@ public MountTableRefresherThread(MountTableManager manager,
    * update cache on R2 and R3.
    */
   @Override
-  public void run() {
+  public void work() {
     try {
       SecurityUtil.doAsLoginUser(() -> {
         if (UserGroupInformation.isSecurityEnabled()) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/Router.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/Router.java
index 14cc47ffa1e..40697fc14a4 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/Router.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/Router.java
@@ -59,6 +59,7 @@
 import org.apache.hadoop.thirdparty.com.google.common.collect.Maps;
 import org.apache.hadoop.util.JvmPauseMonitor;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -391,9 +392,9 @@ protected void serviceStop() throws Exception {
    * Shutdown the router.
    */
   public void shutDown() {
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         Router.this.stop();
       }
     }.start();
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterHeartbeatService.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterHeartbeatService.java
index 5607ab8109d..96c5dd13d9d 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterHeartbeatService.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterHeartbeatService.java
@@ -35,6 +35,7 @@
 import org.apache.hadoop.hdfs.server.federation.store.records.BaseRecord;
 import org.apache.hadoop.hdfs.server.federation.store.records.RouterState;
 import org.apache.hadoop.hdfs.server.federation.store.records.StateStoreVersion;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -63,7 +64,7 @@ public RouterHeartbeatService(Router router) {
    * Trigger the update of the Router state asynchronously.
    */
   protected void updateStateAsync() {
-    Thread thread = new Thread(this::updateStateStore, "Router Heartbeat Async");
+    Thread thread = new SubjectInheritingThread(this::updateStateStore, "Router Heartbeat Async");
     thread.setDaemon(true);
     thread.start();
   }
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java
index 1b8c02fd72e..c0f57336a7a 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java
@@ -214,6 +214,7 @@
 import org.apache.hadoop.tools.protocolPB.GetUserMappingsProtocolPB;
 import org.apache.hadoop.tools.protocolPB.GetUserMappingsProtocolServerSideTranslatorPB;
 import org.apache.hadoop.util.ReflectionUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -2507,7 +2508,7 @@ private static class AsyncThreadFactory implements ThreadFactory {
 
     @Override
     public Thread newThread(@NonNull Runnable r) {
-      Thread thread = new Thread(r, namePrefix + threadNumber.getAndIncrement());
+      Thread thread = new SubjectInheritingThread(r, namePrefix + threadNumber.getAndIncrement());
       thread.setDaemon(true);
       return thread;
     }
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/fairness/TestRouterRefreshFairnessPolicyController.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/fairness/TestRouterRefreshFairnessPolicyController.java
index 3650ce60f72..2eaa71076d0 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/fairness/TestRouterRefreshFairnessPolicyController.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/fairness/TestRouterRefreshFairnessPolicyController.java
@@ -40,6 +40,7 @@
 import org.apache.hadoop.hdfs.server.federation.router.RemoteMethod;
 import org.apache.hadoop.hdfs.server.federation.router.RouterRpcClient;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import static org.apache.hadoop.hdfs.server.federation.router.RBFConfigKeys.DFS_ROUTER_FAIR_HANDLER_COUNT_KEY_PREFIX;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -133,7 +134,7 @@ public void testConcurrentRefreshRequests() throws InterruptedException {
     // Spawn 100 concurrent refresh requests
     Thread[] threads = new Thread[100];
     for (int i = 0; i < 100; i++) {
-      threads[i] = new Thread(() ->
+      threads[i] = new SubjectInheritingThread(() ->
           client.refreshFairnessPolicyController(routerContext.getConf()));
     }
 
@@ -182,7 +183,7 @@ public void testRefreshStaticChangeHandlers() throws Exception {
     final int newNs1Permits = 4;
     conf.setInt(DFS_ROUTER_FAIR_HANDLER_COUNT_KEY_PREFIX + "ns0", newNs0Permits);
     conf.setInt(DFS_ROUTER_FAIR_HANDLER_COUNT_KEY_PREFIX + "ns1", newNs1Permits);
-    Thread threadRefreshController = new Thread(() -> client.
+    Thread threadRefreshController = new SubjectInheritingThread(() -> client.
         refreshFairnessPolicyController(routerContext.getConf()));
     threadRefreshController.start();
     threadRefreshController.join();
@@ -218,7 +219,7 @@ private List<Thread> makeDummyInvocations(RouterRpcClient client, final int nThr
     RemoteMethod dummyMethod = Mockito.mock(RemoteMethod.class);
     List<Thread> threadAcquirePermits = new ArrayList<>();
     for (int i = 0; i < nThreads; i++) {
-      Thread threadAcquirePermit = new Thread(() -> {
+      Thread threadAcquirePermit = new SubjectInheritingThread(() -> {
         try {
           client.invokeSingle(namespace, dummyMethod);
         } catch (IOException e) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterFederationRename.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterFederationRename.java
index b43c87591d7..0036d9044d0 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterFederationRename.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterFederationRename.java
@@ -45,6 +45,7 @@
 import org.apache.hadoop.security.GroupMappingServiceProvider;
 import org.apache.hadoop.test.LambdaTestUtils;
 import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableSet;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.BeforeAll;
@@ -318,7 +319,7 @@ public void testCounter() throws Exception {
     int expectedSchedulerCount = rpcServer.getSchedulerJobCount() + 1;
     AtomicInteger maxSchedulerCount = new AtomicInteger();
     AtomicBoolean watch = new AtomicBoolean(true);
-    Thread watcher = new Thread(() -> {
+    Thread watcher = new SubjectInheritingThread(() -> {
       while (watch.get()) {
         int schedulerCount = rpcServer.getSchedulerJobCount();
         if (schedulerCount > maxSchedulerCount.get()) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableCacheRefresh.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableCacheRefresh.java
index 373743299f0..3857b8ebbfa 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableCacheRefresh.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableCacheRefresh.java
@@ -324,7 +324,7 @@ protected MountTableRefresherThread getLocalRefresher(
               String adminAddress) {
             return new MountTableRefresherThread(null, adminAddress) {
               @Override
-              public void run() {
+              public void work() {
                 try {
                   // Sleep 1 minute
                   Thread.sleep(60000);
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterRpc.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterRpc.java
index ddbfdc9727c..648104a01e9 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterRpc.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterRpc.java
@@ -152,6 +152,7 @@
 import java.util.stream.Collectors;
 
 import org.apache.hadoop.thirdparty.com.google.common.collect.Maps;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 /**
  * The the RPC interface of the {@link Router} implemented by
@@ -2393,7 +2394,7 @@ public void testCallerContextNotResetByAsyncHandler() throws IOException {
     String dirPath = "/test";
 
     // The reason we start this child thread is that CallContext use InheritableThreadLocal.
-    Thread t1 = new Thread(() -> {
+    SubjectInheritingThread t1 = new SubjectInheritingThread(() -> {
       // Set flag async:true.
       CallerContext.setCurrent(
           new CallerContext.Builder("async:true").build());
diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/async/utils/SyncClass.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/async/utils/SyncClass.java
index 805b955661d..51fd4486a6e 100644
--- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/async/utils/SyncClass.java
+++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/async/utils/SyncClass.java
@@ -24,6 +24,8 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
+
 /**
  * SyncClass implements BaseClass, providing a synchronous
  * version of the methods. All operations are performed in a
@@ -186,7 +188,7 @@ public String timeConsumingMethod(int input) {
 
   private ExecutorService getExecutorService() {
     return Executors.newFixedThreadPool(2, r -> {
-      Thread t = new Thread(r);
+      SubjectInheritingThread t = new SubjectInheritingThread(r);
       t.setDaemon(true);
       return t;
     });
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java
index ed29578ee0c..1d680702cdc 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java
@@ -130,7 +130,7 @@
 import org.apache.hadoop.util.LightWeightGSet;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
-
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.util.Preconditions;
 
@@ -5641,7 +5641,7 @@ public int getBlockOpQueueLength() {
     return blockReportThread.queue.size();
   }
 
-  private class BlockReportProcessingThread extends Thread {
+  private class BlockReportProcessingThread extends SubjectInheritingThread {
     private long lastFull = 0;
 
     private final BlockingQueue<Runnable> queue;
@@ -5653,7 +5653,7 @@ private class BlockReportProcessingThread extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         processQueue();
       } catch (Throwable t) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/CacheReplicationMonitor.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/CacheReplicationMonitor.java
index 83c179bfe65..066d6d9a5c2 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/CacheReplicationMonitor.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/CacheReplicationMonitor.java
@@ -53,6 +53,7 @@
 import org.apache.hadoop.hdfs.util.RwLockMode;
 import org.apache.hadoop.util.GSet;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -65,7 +66,7 @@
  * starts up, and at configurable intervals afterwards.
  */
 @InterfaceAudience.LimitedPrivate({"HDFS"})
-public class CacheReplicationMonitor extends Thread implements Closeable {
+public class CacheReplicationMonitor extends SubjectInheritingThread implements Closeable {
 
   private static final Logger LOG =
       LoggerFactory.getLogger(CacheReplicationMonitor.class);
@@ -159,7 +160,7 @@ public CacheReplicationMonitor(FSNamesystem namesystem,
   }
 
   @Override
-  public void run() {
+  public void work() {
     long startTimeMs = 0;
     Thread.currentThread().setName("CacheReplicationMonitor(" +
         System.identityHashCode(this) + ")");
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/SlowDiskTracker.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/SlowDiskTracker.java
index 798b5fb5966..4526d14a73b 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/SlowDiskTracker.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/SlowDiskTracker.java
@@ -34,6 +34,7 @@
 import org.apache.hadoop.hdfs.server.protocol.SlowDiskReports.DiskOp;
 import org.apache.hadoop.util.Lists;
 import org.apache.hadoop.util.Timer;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -152,7 +153,7 @@ public void checkAndUpdateReportIfNecessary() {
   public void updateSlowDiskReportAsync(long now) {
     if (isUpdateInProgress.compareAndSet(false, true)) {
       lastUpdateTime = now;
-      new Thread(new Runnable() {
+      new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           slowDisksReport = getSlowDisks(diskIDLatencyMap,
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/Storage.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/Storage.java
index 93303bcf807..738eded1730 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/Storage.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/Storage.java
@@ -53,6 +53,7 @@
 import org.apache.hadoop.io.nativeio.NativeIOException;
 import org.apache.hadoop.util.ToolRunner;
 import org.apache.hadoop.util.VersionInfo;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.apache.hadoop.util.Preconditions;
 import org.slf4j.Logger;
@@ -849,8 +850,8 @@ private void deleteAsync(File curDir) throws IOException {
           deleteDir(curTmp);
         }
         rename(curDir, curTmp);
-        new Thread("Async Delete Current.tmp") {
-          public void run() {
+        new SubjectInheritingThread("Async Delete Current.tmp") {
+          public void work() {
             try {
               deleteDir(curTmp);
             } catch (IOException e) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPServiceActor.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPServiceActor.java
index 45eeac85d6b..2ceca34edd9 100755
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPServiceActor.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPServiceActor.java
@@ -75,6 +75,7 @@
 import org.apache.hadoop.util.Time;
 import org.apache.hadoop.util.VersionInfo;
 import org.apache.hadoop.util.VersionUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 
 import org.apache.hadoop.classification.VisibleForTesting;
@@ -599,7 +600,7 @@ void start() {
       //Thread is started already
       return;
     }
-    bpThread = new Thread(this);
+    bpThread = new SubjectInheritingThread(this);
     bpThread.setDaemon(true); // needed for JUnit testing
 
     if (lifelineSender != null) {
@@ -1078,7 +1079,7 @@ public void run() {
     }
 
     public void start() {
-      lifelineThread = new Thread(this,
+      lifelineThread = new SubjectInheritingThread(this,
           formatThreadName("lifeline", lifelineNnAddr));
       lifelineThread.setDaemon(true);
       lifelineThread.setUncaughtExceptionHandler(
@@ -1384,7 +1385,7 @@ public long monotonicNow() {
   /**
    * CommandProcessingThread that process commands asynchronously.
    */
-  class CommandProcessingThread extends Thread {
+  class CommandProcessingThread extends SubjectInheritingThread {
     private final BPServiceActor actor;
     private final BlockingQueue<Runnable> queue;
 
@@ -1396,7 +1397,7 @@ class CommandProcessingThread extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         processQueue();
       } catch (Throwable t) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DataNode.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DataNode.java
index ef778791cfd..3a1b1e07f36 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DataNode.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DataNode.java
@@ -262,6 +262,7 @@
 import org.apache.hadoop.tracing.TraceUtils;
 import org.apache.hadoop.util.DiskChecker.DiskErrorException;
 import org.apache.hadoop.util.concurrent.HadoopExecutors;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.tracing.Tracer;
 import org.eclipse.jetty.util.ajax.JSON;
 
@@ -3855,8 +3856,8 @@ public synchronized void shutdownDatanode(boolean forUpgrade) throws IOException
 
     // Asynchronously start the shutdown process so that the rpc response can be
     // sent back.
-    Thread shutdownThread = new Thread("Async datanode shutdown thread") {
-      @Override public void run() {
+    Thread shutdownThread = new SubjectInheritingThread("Async datanode shutdown thread") {
+      @Override public void work() {
         if (!shutdownForUpgrade) {
           // Delay the shutdown a bit if not doing for restart.
           try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/VolumeScanner.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/VolumeScanner.java
index d8f1e23ec37..77e30b85675 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/VolumeScanner.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/VolumeScanner.java
@@ -45,6 +45,7 @@
 import org.apache.hadoop.hdfs.util.DataTransferThrottler;
 import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -52,7 +53,7 @@
  * VolumeScanner scans a single volume.  Each VolumeScanner has its own thread.
  * <p>They are all managed by the DataNode's BlockScanner.
  */
-public class VolumeScanner extends Thread {
+public class VolumeScanner extends SubjectInheritingThread {
   public static final Logger LOG =
       LoggerFactory.getLogger(VolumeScanner.class);
 
@@ -633,7 +634,7 @@ private synchronized ExtendedBlock popNextSuspectBlock() {
   }
 
   @Override
-  public void run() {
+  public void work() {
     // Record the minute on which the scanner started.
     this.startMinute =
         TimeUnit.MINUTES.convert(Time.monotonicNow(), TimeUnit.MILLISECONDS);
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsDatasetAsyncDiskService.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsDatasetAsyncDiskService.java
index e5b23bb60e5..f6caea96346 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsDatasetAsyncDiskService.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsDatasetAsyncDiskService.java
@@ -33,6 +33,7 @@
 import org.apache.hadoop.hdfs.DFSConfigKeys;
 import org.apache.hadoop.hdfs.server.datanode.DataNodeFaultInjector;
 import org.apache.hadoop.util.Preconditions;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
@@ -109,7 +110,7 @@ public Thread newThread(Runnable r) {
         synchronized (this) {
           thisIndex = counter++;
         }
-        Thread t = new Thread(r);
+        Thread t = new SubjectInheritingThread(r);
         t.setName("Async disk worker #" + thisIndex +
             " for volume " + volume);
         return t;
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsVolumeList.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsVolumeList.java
index 262a24bd3aa..a3d54865de0 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsVolumeList.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/FsVolumeList.java
@@ -49,6 +49,7 @@
 import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.util.AutoCloseableLock;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 class FsVolumeList {
   private final CopyOnWriteArrayList<FsVolumeImpl> volumes =
@@ -260,8 +261,8 @@ void getAllVolumesMap(final String bpid,
         new ConcurrentHashMap<FsVolumeSpi, IOException>();
     List<Thread> replicaAddingThreads = new ArrayList<Thread>();
     for (final FsVolumeImpl v : volumes) {
-      Thread t = new Thread() {
-        public void run() {
+      Thread t = new SubjectInheritingThread() {
+        public void work() {
           try (FsVolumeReference ref = v.obtainReference()) {
             FsDatasetImpl.LOG.info("Adding replicas to map for block pool " +
                 bpid + " on volume " + v + "...");
@@ -507,8 +508,8 @@ void addBlockPool(final String bpid, final Configuration conf) throws IOExceptio
         new ConcurrentHashMap<FsVolumeSpi, IOException>();
     List<Thread> blockPoolAddingThreads = new ArrayList<Thread>();
     for (final FsVolumeImpl v : volumes) {
-      Thread t = new Thread() {
-        public void run() {
+      Thread t = new SubjectInheritingThread() {
+        public void work() {
           try (FsVolumeReference ref = v.obtainReference()) {
             FsDatasetImpl.LOG.info("Scanning block pool " + bpid +
                 " on volume " + v + "...");
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/RamDiskAsyncLazyPersistService.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/RamDiskAsyncLazyPersistService.java
index 0d42ae99e35..02b78b24ca2 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/RamDiskAsyncLazyPersistService.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/RamDiskAsyncLazyPersistService.java
@@ -19,6 +19,7 @@
 package org.apache.hadoop.hdfs.server.datanode.fsdataset.impl;
 
 import org.apache.hadoop.io.IOUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.conf.Configuration;
@@ -82,7 +83,7 @@ private void addExecutorForVolume(final String storageId) {
 
       @Override
       public Thread newThread(Runnable r) {
-        Thread t = new Thread(threadGroup, r);
+        Thread t = new SubjectInheritingThread(threadGroup, r);
         t.setName("Async RamDisk lazy persist worker " +
             " for volume with id " + storageId);
         return t;
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLogAsync.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLogAsync.java
index 115e9485fa0..8316a4c5229 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLogAsync.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLogAsync.java
@@ -30,6 +30,7 @@
 
 import org.apache.hadoop.hdfs.server.namenode.metrics.NameNodeMetrics;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.conf.Configuration;
@@ -78,7 +79,7 @@ private boolean isSyncThreadAlive() {
   private void startSyncThread() {
     synchronized(syncThreadLock) {
       if (!isSyncThreadAlive()) {
-        syncThread = new Thread(this, this.getClass().getSimpleName());
+        syncThread = new SubjectInheritingThread(this, this.getClass().getSimpleName());
         syncThread.start();
       }
     }
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java
index fa321fe85e5..e6a6fbfc0a8 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java
@@ -76,6 +76,7 @@
 import org.apache.hadoop.util.ExitUtil;
 import org.apache.hadoop.util.Lists;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.apache.hadoop.thirdparty.com.google.common.base.Joiner;
 import org.apache.hadoop.util.Preconditions;
@@ -1247,7 +1248,7 @@ private synchronized void saveFSImageInAllDirs(FSNamesystem source,
              = storage.dirIterator(NameNodeDirType.IMAGE); it.hasNext();) {
         StorageDirectory sd = it.next();
         FSImageSaver saver = new FSImageSaver(ctx, sd, nnf);
-        Thread saveThread = new Thread(saver, saver.toString());
+        Thread saveThread = new SubjectInheritingThread(saver, saver.toString());
         saveThreads.add(saveThread);
         saveThread.start();
       }
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImageFormatProtobuf.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImageFormatProtobuf.java
index edacb7eaafd..c84f3266dcf 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImageFormatProtobuf.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImageFormatProtobuf.java
@@ -75,6 +75,7 @@
 import org.apache.hadoop.io.compress.CompressionCodec;
 import org.apache.hadoop.util.LimitInputStream;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.util.Lists;
 
 import org.apache.hadoop.thirdparty.com.google.common.collect.Maps;
@@ -184,7 +185,7 @@ public LoaderContext getLoaderContext() {
      * Thread to compute the MD5 of a file as this can be in parallel while
      * loading the image without interfering much.
      */
-    private static class DigestThread extends Thread {
+    private static class DigestThread extends SubjectInheritingThread {
 
       /**
        * Exception thrown when computing the digest if it cannot be calculated.
@@ -219,7 +220,7 @@ public IOException getException() {
       }
 
       @Override
-      public void run() {
+      public void work() {
         try {
           digest = MD5FileUtils.computeMd5ForFile(file);
         } catch (IOException e) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNode.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNode.java
index d48941203d3..7f1ed204163 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNode.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNode.java
@@ -103,6 +103,7 @@
 import org.apache.hadoop.util.GcTimeMonitor.Builder;
 import org.apache.hadoop.tracing.Tracer;
 import org.apache.hadoop.util.Timer;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -1076,7 +1077,7 @@ public FileSystem run() throws IOException {
             return dfs;
           }
         });
-    this.emptier = new Thread(new Trash(fs, conf).getEmptier(), "Trash Emptier");
+    this.emptier = new SubjectInheritingThread(new Trash(fs, conf).getEmptier(), "Trash Emptier");
     this.emptier.setDaemon(true);
     this.emptier.start();
   }
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/EditLogTailer.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/EditLogTailer.java
index cf416307f47..472f1536526 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/EditLogTailer.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/EditLogTailer.java
@@ -37,6 +37,7 @@
 import org.apache.hadoop.thirdparty.com.google.common.collect.Iterators;
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
 import org.apache.hadoop.util.Timer;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.classification.InterfaceAudience;
@@ -475,7 +476,7 @@ void sleep(long sleepTimeMillis) throws InterruptedException {
    * The thread which does the actual work of tailing edits journals and
    * applying the transactions to the FSNS.
    */
-  private class EditLogTailerThread extends Thread {
+  private class EditLogTailerThread extends SubjectInheritingThread {
     private volatile boolean shouldRun = true;
     
     private EditLogTailerThread() {
@@ -487,7 +488,7 @@ private void setShouldRun(boolean shouldRun) {
     }
     
     @Override
-    public void run() {
+    public void work() {
       SecurityUtil.doAsLoginUserOrFatal(
           new PrivilegedAction<Object>() {
           @Override
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/StandbyCheckpointer.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/StandbyCheckpointer.java
index e240921f670..e93a384c99d 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/StandbyCheckpointer.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/StandbyCheckpointer.java
@@ -50,6 +50,7 @@
 
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.util.Preconditions;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
 
 import org.slf4j.Logger;
@@ -386,7 +387,7 @@ private long countUncheckpointedTxns() {
       img.getStorage().getMostRecentCheckpointTxId();
   }
 
-  private class CheckpointerThread extends Thread {
+  private class CheckpointerThread extends SubjectInheritingThread {
     private volatile boolean shouldRun = true;
     private volatile long preventCheckpointsUntil = 0;
 
@@ -399,7 +400,7 @@ private void setShouldRun(boolean shouldRun) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       // We have to make sure we're logged in as far as JAAS
       // is concerned, in order to use kerberized SSL properly.
       SecurityUtil.doAsLoginUserOrFatal(
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestAppendSnapshotTruncate.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestAppendSnapshotTruncate.java
index 9a4d866e117..20b2641648c 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestAppendSnapshotTruncate.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestAppendSnapshotTruncate.java
@@ -51,6 +51,7 @@
 import org.junit.jupiter.api.Test;
 
 import org.apache.hadoop.util.Preconditions;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.event.Level;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -448,7 +449,7 @@ void start() {
       Preconditions.checkState(state.compareAndSet(State.IDLE, State.RUNNING));
       
       if (thread.get() == null) {
-        final Thread t = new Thread(null, new Runnable() {
+        final Thread t = new SubjectInheritingThread(null, new Runnable() {
           @Override
           public void run() {
             for(State s; !(s = checkErrorState()).isTerminated;) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestClientProtocolForPipelineRecovery.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestClientProtocolForPipelineRecovery.java
index f277b1a37b8..5462d190c10 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestClientProtocolForPipelineRecovery.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestClientProtocolForPipelineRecovery.java
@@ -57,6 +57,7 @@
 import org.apache.hadoop.hdfs.tools.DFSAdmin;
 import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.mockito.Mockito;
@@ -521,8 +522,8 @@ public boolean skipRollingRestartWait() {
           .getWrappedStream();
       final AtomicBoolean running = new AtomicBoolean(true);
       final AtomicBoolean failed = new AtomicBoolean(false);
-      Thread t = new Thread() {
-        public void run() {
+      SubjectInheritingThread t = new SubjectInheritingThread() {
+        public void work() {
           while (running.get()) {
             try {
               out.write("test".getBytes());
@@ -866,7 +867,7 @@ public Boolean get() {
       dataNodes[0].shutdown();
 
       // Shutdown the second datanode when the pipeline is closing.
-      new Thread(() -> {
+      new SubjectInheritingThread(() -> {
         try {
           GenericTestUtils.waitFor(new Supplier<Boolean>() {
             @Override
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSClientRetries.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSClientRetries.java
index 8eb2f588228..a1eb0c56af9 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSClientRetries.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSClientRetries.java
@@ -90,6 +90,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -729,7 +730,7 @@ private boolean busyTest(int xcievers, int threads, int fileLen, int timeWin, in
       Counter counter = new Counter(0);
       for (int i = 0; i < threads; ++i ) {
         DFSClientReader reader = new DFSClientReader(file1, cluster, hash_sha, fileLen, counter);
-        readers[i] = new Thread(reader);
+        readers[i] = new SubjectInheritingThread(reader);
         readers[i].start();
       }
       
@@ -1018,7 +1019,7 @@ public static void namenodeRestartTest(final Configuration conf,
       assertFalse(HdfsUtils.isHealthy(uri));
 
       //namenode is down, continue writing file4 in a thread
-      final Thread file4thread = new Thread(new Runnable() {
+      final Thread file4thread = new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           try {
@@ -1037,7 +1038,7 @@ public void run() {
       file4thread.start();
 
       //namenode is down, read the file in a thread
-      final Thread reader = new Thread(new Runnable() {
+      final Thread reader = new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           try {
@@ -1057,7 +1058,7 @@ public void run() {
 
       //namenode is down, create another file in a thread
       final Path file3 = new Path(dir, "file"); 
-      final Thread thread = new Thread(new Runnable() {
+      final Thread thread = new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           try {
@@ -1072,7 +1073,7 @@ public void run() {
       thread.start();
 
       //restart namenode in a new thread
-      new Thread(new Runnable() {
+      new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           try {
@@ -1125,7 +1126,7 @@ public void run() {
       assertFalse(HdfsUtils.isHealthy(uri));
       
       //leave safe mode in a new thread
-      new Thread(new Runnable() {
+      new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           try {
@@ -1306,7 +1307,7 @@ public void delayWhenRenewLeaseTimeout() {
 
       out1.write(new byte[256]);
 
-      Thread closeThread = new Thread(new Runnable() {
+      Thread closeThread = new SubjectInheritingThread(new Runnable() {
         @Override public void run() {
           try {
             //1. trigger get LeaseRenewer lock
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSOutputStream.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSOutputStream.java
index d52b53d5432..f82bb16ee84 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSOutputStream.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSOutputStream.java
@@ -60,6 +60,7 @@
 import org.apache.hadoop.test.PathUtils;
 import org.apache.hadoop.test.Whitebox;
 import org.apache.hadoop.util.DataChecksum;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -339,7 +340,7 @@ public void testCongestionAckDelay() {
     AtomicBoolean isDelay = new AtomicBoolean(true);
 
     // ResponseProcessor needs the dataQueue for the next step.
-    new Thread(() -> {
+    new SubjectInheritingThread(() -> {
       for (int i = 0; i < 10; i++) {
         // In order to ensure that other threads run for a period of time to prevent affecting
         // the results.
@@ -376,7 +377,7 @@ public void testCongestionAckDelay() {
 
     // The purpose of adding packets to the dataQueue is to make the DataStreamer run
     // normally and judge whether to enter the sleep state according to the congestion.
-    new Thread(() -> {
+    new SubjectInheritingThread(() -> {
       for (int i = 0; i < 100; i++) {
         packet[i] = mock(DFSPacket.class);
         dataQueue.add(packet[i]);
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSShell.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSShell.java
index 2598dd44a37..3188185a3e1 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSShell.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDFSShell.java
@@ -70,6 +70,7 @@
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.slf4j.event.Level;
@@ -948,9 +949,9 @@ public void testTailWithFresh() throws Exception {
 
     final ByteArrayOutputStream out = new ByteArrayOutputStream();
     System.setOut(new PrintStream(out));
-    final Thread tailer = new Thread() {
+    final SubjectInheritingThread tailer = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         final String[] argv = new String[]{"-tail", "-f",
             testFile.toString()};
         try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDatanodeDeath.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDatanodeDeath.java
index c9c7020ed6c..1b49267ab89 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDatanodeDeath.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDatanodeDeath.java
@@ -34,6 +34,7 @@
 import org.apache.hadoop.hdfs.server.datanode.DataNode;
 import org.apache.hadoop.hdfs.server.protocol.InterDatanodeProtocol;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.slf4j.event.Level;
 
@@ -61,7 +62,7 @@ public class TestDatanodeDeath {
   //
   // an object that does a bunch of transactions
   //
-  static class Workload extends Thread {
+  static class Workload extends SubjectInheritingThread {
     private final short replication;
     private final int numberOfFiles;
     private final int id;
@@ -81,7 +82,7 @@ static class Workload extends Thread {
 
     // create a bunch of files. Write to them and then verify.
     @Override
-    public void run() {
+    public void work() {
       System.out.println("Workload starting ");
       for (int i = 0; i < numberOfFiles; i++) {
         Path filename = new Path(id + "." + i);
@@ -210,7 +211,7 @@ private static void checkData(byte[] actual, int from, byte[] expected, String m
    * a block do not get killed (otherwise the file will be corrupt and the
    * test will fail).
    */
-  class Modify extends Thread {
+  class Modify extends SubjectInheritingThread {
     volatile boolean running;
     final MiniDFSCluster cluster;
     final Configuration conf;
@@ -222,7 +223,7 @@ class Modify extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
 
       while (running) {
         try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDeadNodeDetection.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDeadNodeDetection.java
index df3fc4f8c43..c7436f0f2fe 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDeadNodeDetection.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDeadNodeDetection.java
@@ -26,6 +26,7 @@
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -456,7 +457,7 @@ public void sync() {
     }
 
     private void startWaitForDeadNodeThread(DFSClient dfsClient, int size) {
-      new Thread(() -> {
+      new SubjectInheritingThread(() -> {
         DeadNodeDetector deadNodeDetector =
             dfsClient.getClientContext().getDeadNodeDetector();
         while (deadNodeDetector.clearAndGetDetectedDeadNodes().size() != size) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDecommission.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDecommission.java
index 63f8dc22698..9715083c373 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDecommission.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDecommission.java
@@ -86,6 +86,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Lists;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -864,7 +865,7 @@ public void testDecommissionWithOpenfileReporting()
         closedFileSet, openFilesMap, maxDnOccurance);
 
     final AtomicBoolean stopRedundancyMonitor = new AtomicBoolean(false);
-    Thread monitorThread = new Thread(new Runnable() {
+    Thread monitorThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         while (!stopRedundancyMonitor.get()) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDecommissionWithStriped.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDecommissionWithStriped.java
index 16e1ea25b4b..8a8361661f0 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDecommissionWithStriped.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDecommissionWithStriped.java
@@ -64,6 +64,7 @@
 import org.apache.hadoop.hdfs.server.namenode.NameNodeAdapter;
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.MethodOrderer;
@@ -259,8 +260,8 @@ public void testDecommissionWithURBlockForSameBlockGroup() throws Exception {
 
     // Decommission node in a new thread. Verify that node is decommissioned.
     final CountDownLatch decomStarted = new CountDownLatch(0);
-    Thread decomTh = new Thread() {
-      public void run() {
+    SubjectInheritingThread decomTh = new SubjectInheritingThread() {
+      public void work() {
         try {
           decomStarted.countDown();
           decommissionNode(0, decommisionNodes, AdminStates.DECOMMISSIONED);
@@ -995,7 +996,7 @@ public void testDecommissionWithMissingBlock() throws Exception {
     // Handle decommission nodes in a new thread.
     // Verify that nodes are decommissioned.
     final CountDownLatch decomStarted = new CountDownLatch(0);
-    new Thread(
+    new SubjectInheritingThread(
         () -> {
           try {
             decomStarted.countDown();
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend2.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend2.java
index b7916ec5607..f74ac5ee332 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend2.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend2.java
@@ -42,6 +42,7 @@
 import org.apache.hadoop.security.AccessControlException;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.slf4j.event.Level;
 
@@ -372,7 +373,7 @@ public void testSimpleAppend2() throws Exception {
   //
   // an object that does a bunch of appends to files
   //
-  class Workload extends Thread {
+  class Workload extends SubjectInheritingThread {
     private final int id;
     private final MiniDFSCluster cluster;
     private final boolean appendToNewBlock;
@@ -385,7 +386,7 @@ class Workload extends Thread {
 
     // create a bunch of files. Write to them and then verify.
     @Override
-    public void run() {
+    public void work() {
       System.out.println("Workload " + id + " starting... ");
       for (int i = 0; i < numAppendsPerThread; i++) {
    
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend3.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend3.java
index 5b3a96305a7..936a90d9e4d 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend3.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend3.java
@@ -27,6 +27,7 @@
 
 import org.apache.hadoop.fs.CreateFlag;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.mockito.invocation.InvocationOnMock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
@@ -552,9 +553,9 @@ public HdfsFileStatus answer(InvocationOnMock invocation){
     DFSClientAdapter.setDFSClient(fs, spyClient);
 
     // Create two threads for doing appends to the same file.
-    Thread worker1 = new Thread() {
+    SubjectInheritingThread worker1 = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           doSmallAppends(file, fs, 20);
         } catch (IOException e) {
@@ -562,9 +563,9 @@ public void run() {
       }
     };
 
-    Thread worker2 = new Thread() {
+    SubjectInheritingThread worker2 = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           doSmallAppends(file, fs, 20);
         } catch (IOException e) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend4.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend4.java
index b10439b248b..52182f98dda 100755
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend4.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileAppend4.java
@@ -47,6 +47,7 @@
 import org.apache.hadoop.hdfs.server.namenode.LeaseExpiredException;
 import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -165,9 +166,9 @@ public void testRecoverFinalizedBlock() throws Throwable {
       // write 1/2 block
       AppendTestUtil.write(stm, 0, 4096);
       final AtomicReference<Throwable> err = new AtomicReference<Throwable>();
-      Thread t = new Thread() { 
+      SubjectInheritingThread t = new SubjectInheritingThread() {
           @Override
-          public void run() {
+          public void work() {
             try {
               stm.close();
             } catch (Throwable t) {
@@ -237,9 +238,9 @@ public void testCompleteOtherLeaseHoldersFile() throws Throwable {
       // write 1/2 block
       AppendTestUtil.write(stm, 0, 4096);
       final AtomicReference<Throwable> err = new AtomicReference<Throwable>();
-      Thread t = new Thread() { 
+      SubjectInheritingThread t = new SubjectInheritingThread() {
           @Override
-          public void run() {
+          public void work() {
             try {
               stm.close();
             } catch (Throwable t) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileConcurrentReader.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileConcurrentReader.java
index a8d3c52fc6f..77126a99d15 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileConcurrentReader.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileConcurrentReader.java
@@ -38,6 +38,7 @@
 import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
@@ -221,7 +222,7 @@ public void testImmediateReadOfNewFile()
     final AtomicReference<String> errorMessage = new AtomicReference<String>();
     final FSDataOutputStream out = fileSystem.create(file);
     
-    final Thread writer = new Thread(new Runnable() {
+    final Thread writer = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
@@ -241,7 +242,7 @@ public void run() {
       }
     });
     
-    Thread opener = new Thread(new Runnable() {
+    Thread opener = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
@@ -346,7 +347,7 @@ private void runTestUnfinishedBlockCRCError(
     final AtomicBoolean writerStarted = new AtomicBoolean(false);
     final AtomicBoolean error = new AtomicBoolean(false);
 
-    final Thread writer = new Thread(new Runnable() {
+    final Thread writer = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
@@ -379,7 +380,7 @@ public void run() {
         }
       }
     });
-    Thread tailer = new Thread(new Runnable() {
+    Thread tailer = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileCreationClient.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileCreationClient.java
index 41b5b340c88..6d1d6312515 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileCreationClient.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestFileCreationClient.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.hdfs.server.protocol.InterDatanodeProtocol;
 import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.slf4j.event.Level;
 
@@ -113,7 +114,7 @@ public void testClientTriggeredLeaseRecovery() throws Exception {
     }
   }
 
-  static class SlowWriter extends Thread {
+  static class SlowWriter extends SubjectInheritingThread {
     final FileSystem fs;
     final Path filepath;
     boolean running = true;
@@ -125,7 +126,7 @@ static class SlowWriter extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       FSDataOutputStream out = null;
       int i = 0;
       try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestMultiThreadedHflush.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestMultiThreadedHflush.java
index 48666f68dfd..023013c02dd 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestMultiThreadedHflush.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestMultiThreadedHflush.java
@@ -35,6 +35,7 @@
 import org.apache.hadoop.util.StopWatch;
 import org.apache.hadoop.util.Tool;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -71,7 +72,7 @@ private void initBuffer(int size) {
     toWrite = AppendTestUtil.randomBytes(seed, size);
   }
 
-  private class WriterThread extends Thread {
+  private class WriterThread extends SubjectInheritingThread {
     private final FSDataOutputStream stm;
     private final AtomicReference<Throwable> thrown;
     private final int numWrites;
@@ -87,7 +88,7 @@ public WriterThread(FSDataOutputStream stm,
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         countdown.await();
         for (int i = 0; i < numWrites && thrown.get() == null; i++) {
@@ -162,9 +163,9 @@ public void testHflushWhileClosing() throws Throwable {
     final AtomicReference<Throwable> thrown = new AtomicReference<Throwable>();
     try {
       for (int i = 0; i < 10; i++) {
-        Thread flusher = new Thread() {
+        SubjectInheritingThread flusher = new SubjectInheritingThread() {
             @Override
-            public void run() {
+            public void work() {
               try {
                 while (true) {
                   try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestParallelReadUtil.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestParallelReadUtil.java
index 06d8aec4ffc..7536d7d4870 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestParallelReadUtil.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestParallelReadUtil.java
@@ -33,6 +33,7 @@
 import org.apache.hadoop.hdfs.client.impl.BlockReaderTestUtil;
 import org.apache.hadoop.hdfs.server.datanode.DataNode;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.slf4j.event.Level;
 
@@ -189,7 +190,7 @@ public int pRead(DFSInputStream dis, byte[] target, int startOff, int len)
   /**
    * A worker to do one "unit" of read.
    */
-  static class ReadWorker extends Thread {
+  static class ReadWorker extends SubjectInheritingThread {
 
     static public final int N_ITERATIONS = 1024;
 
@@ -215,7 +216,7 @@ static class ReadWorker extends Thread {
      * Randomly do one of (1) Small read; and (2) Large Pread.
      */
     @Override
-    public void run() {
+    public void work() {
       for (int i = 0; i < N_ITERATIONS; ++i) {
         int startOff = rand.nextInt((int) fileSize);
         int len = 0;
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestRead.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestRead.java
index 8f5ba9018df..9b1b6c50b7e 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestRead.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestRead.java
@@ -34,6 +34,7 @@
 import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsDatasetSpi;
 import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.log4j.Level;
 import org.junit.jupiter.api.Assertions;
 
@@ -159,7 +160,7 @@ public void testInterruptReader() throws Exception {
 
       final FSDataInputStream in = fs.open(file);
       AtomicBoolean readInterrupted = new AtomicBoolean(false);
-      final Thread reader = new Thread(new Runnable() {
+      final Thread reader = new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReplaceDatanodeFailureReplication.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReplaceDatanodeFailureReplication.java
index a52eba866c0..995652ef61c 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReplaceDatanodeFailureReplication.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReplaceDatanodeFailureReplication.java
@@ -31,6 +31,7 @@
 import org.apache.hadoop.hdfs.protocol.datatransfer.ReplaceDatanodeOnFailure;
 import org.apache.hadoop.hdfs.protocol.datatransfer.ReplaceDatanodeOnFailure.Policy;
 import org.apache.hadoop.io.IOUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -244,7 +245,7 @@ static void sleepSeconds(final int waittime) throws InterruptedException {
     Thread.sleep(waittime * 1000L);
   }
 
-  static class SlowWriter extends Thread {
+  static class SlowWriter extends SubjectInheritingThread {
     private final Path filepath;
     private final HdfsDataOutputStream out;
     private final long sleepms;
@@ -258,7 +259,7 @@ static class SlowWriter extends Thread {
       this.sleepms = sleepms;
     }
 
-    @Override public void run() {
+    @Override public void work() {
       int i = 0;
       try {
         sleep(sleepms);
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReplaceDatanodeOnFailure.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReplaceDatanodeOnFailure.java
index e2df6090955..966b585ec44 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReplaceDatanodeOnFailure.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestReplaceDatanodeOnFailure.java
@@ -37,6 +37,7 @@
 import org.apache.hadoop.hdfs.protocol.datatransfer.ReplaceDatanodeOnFailure.Policy;
 import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.slf4j.event.Level;
@@ -222,7 +223,7 @@ static void sleepSeconds(final int waittime) throws InterruptedException {
     Thread.sleep(waittime * 1000L);
   }
 
-  static class SlowWriter extends Thread {
+  static class SlowWriter extends SubjectInheritingThread {
     final Path filepath;
     final HdfsDataOutputStream out;
     final long sleepms;
@@ -237,7 +238,7 @@ static class SlowWriter extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       int i = 0;
 
       try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/client/impl/TestBlockReaderFactory.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/client/impl/TestBlockReaderFactory.java
index 810f7e1864d..a7392d2acd6 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/client/impl/TestBlockReaderFactory.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/client/impl/TestBlockReaderFactory.java
@@ -73,6 +73,7 @@
 import org.junit.jupiter.api.Timeout;
 
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.Uninterruptibles;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -235,7 +236,7 @@ public void run() {
     };
     Thread threads[] = new Thread[NUM_THREADS];
     for (int i = 0; i < NUM_THREADS; i++) {
-      threads[i] = new Thread(readerRunnable);
+      threads[i] = new SubjectInheritingThread(readerRunnable);
       threads[i].start();
     }
     Thread.sleep(500);
@@ -334,7 +335,7 @@ public void run() {
     };
     Thread threads[] = new Thread[NUM_THREADS];
     for (int i = 0; i < NUM_THREADS; i++) {
-      threads[i] = new Thread(readerRunnable);
+      threads[i] = new SubjectInheritingThread(readerRunnable);
       threads[i].start();
     }
     gotFailureLatch.await();
@@ -640,7 +641,7 @@ public void run() {
         }
       }
     };
-    Thread thread = new Thread(readerRunnable);
+    Thread thread = new SubjectInheritingThread(readerRunnable);
     thread.start();
 
     // While the thread is reading, send it interrupts.
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/balancer/TestBalancerService.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/balancer/TestBalancerService.java
index 2149b924d12..a8d09ab1598 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/balancer/TestBalancerService.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/balancer/TestBalancerService.java
@@ -34,6 +34,7 @@
 import org.apache.hadoop.test.MetricsAsserts;
 import org.apache.hadoop.util.Tool;
 import org.apache.hadoop.util.VersionInfo;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -106,7 +107,7 @@ private long addOneDataNode(Configuration conf) throws Exception {
   }
 
   private Thread newBalancerService(Configuration conf, String[] args) {
-    return new Thread(new Runnable() {
+    return new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         Tool cli = new Balancer.Cli();
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/blockmanagement/TestBlockManager.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/blockmanagement/TestBlockManager.java
index 0ef9c4e3cb5..c289a611d0c 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/blockmanagement/TestBlockManager.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/blockmanagement/TestBlockManager.java
@@ -29,6 +29,7 @@
 import org.apache.hadoop.hdfs.server.datanode.DataNodeTestUtils;
 import org.apache.hadoop.hdfs.server.namenode.NameNode;
 import org.apache.hadoop.util.Lists;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.CreateFlag;
@@ -1524,7 +1525,7 @@ public void testAsyncIBR() throws Exception {
       Thread[] writers = new Thread[numWriters];
       for (int i=0; i < writers.length; i++) {
         final Path p = new Path("/writer"+i);
-        writers[i] = new Thread(new Runnable() {
+        writers[i] = new SubjectInheritingThread(new Runnable() {
           @Override
           public void run() {
             try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/BlockReportTestBase.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/BlockReportTestBase.java
index 0dfe3ae5097..5e748d5e021 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/BlockReportTestBase.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/BlockReportTestBase.java
@@ -70,6 +70,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.test.GenericTestUtils.DelayAnswer;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -915,7 +916,7 @@ private Block findBlock(Path path, long size) throws IOException {
     return ret;
   }
 
-  private class BlockChecker extends Thread {
+  private class BlockChecker extends SubjectInheritingThread {
     final Path filePath;
 
     public BlockChecker(final Path filePath) {
@@ -923,7 +924,7 @@ public BlockChecker(final Path filePath) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         startDNandWait(filePath, true);
       } catch (Exception e) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBPOfferService.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBPOfferService.java
index da89b85f6de..9b080888478 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBPOfferService.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBPOfferService.java
@@ -87,6 +87,7 @@
 import org.apache.hadoop.test.PathUtils;
 import org.apache.hadoop.util.Lists;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
@@ -337,7 +338,7 @@ public void blockUtilSendFullBlockReport() {
       });
 
       countBlockReportItems(FAKE_BLOCK, mockNN1, blocks);
-      addNewBlockThread = new Thread(() -> {
+      addNewBlockThread = new SubjectInheritingThread(() -> {
         for (int i = 0; i < totalTestBlocks; i++) {
           SimulatedFSDataset fsDataset = (SimulatedFSDataset) mockFSDataset;
           SimulatedStorage simulatedStorage = fsDataset.getStorages().get(0);
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBlockRecovery.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBlockRecovery.java
index 570d41a69db..8f298140908 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBlockRecovery.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBlockRecovery.java
@@ -96,6 +96,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.DataChecksum;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.event.Level;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -917,7 +918,7 @@ private void testStopWorker(final TestStopWorkerRunnable tswr)
     final RecoveringBlock recoveringBlock =
         Iterators.get(recoveringBlocks.iterator(), 0);
     final ExtendedBlock block = recoveringBlock.getBlock();
-    Thread slowWriterThread = new Thread(new Runnable() {
+    Thread slowWriterThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
@@ -944,7 +945,7 @@ public void run() {
     progressParent.uninterruptiblyAcquire(60000);
 
     // Start a worker thread which will attempt to stop the writer.
-    Thread stopWriterThread = new Thread(new Runnable() {
+    Thread stopWriterThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBlockRecovery2.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBlockRecovery2.java
index 74d18b55c6c..a51f3864dbb 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBlockRecovery2.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBlockRecovery2.java
@@ -47,6 +47,7 @@
 import org.apache.hadoop.hdfs.server.protocol.NamespaceInfo;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.test.TestName;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -248,7 +249,7 @@ public void testRaceBetweenReplicaRecoveryAndFinalizeBlock()
       final DataNode dataNode = cluster.getDataNodes().get(0);
 
       final AtomicBoolean recoveryInitResult = new AtomicBoolean(true);
-      Thread recoveryThread = new Thread(() -> {
+      Thread recoveryThread = new SubjectInheritingThread(() -> {
         try {
           DatanodeInfo[] locations = block.getLocations();
           final BlockRecoveryCommand.RecoveringBlock recoveringBlock =
@@ -367,7 +368,7 @@ public void testEcRecoverBlocks() throws Throwable {
       // write 5MB File
       AppendTestUtil.write(stm, 0, 1024 * 1024 * 5);
       final AtomicReference<Throwable> err = new AtomicReference<>();
-      Thread t = new Thread(() -> {
+      Thread t = new SubjectInheritingThread(() -> {
         try {
           stm.close();
         } catch (Throwable t1) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataNodeHotSwapVolumes.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataNodeHotSwapVolumes.java
index babce8d5833..40d7e5f86a5 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataNodeHotSwapVolumes.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataNodeHotSwapVolumes.java
@@ -51,6 +51,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Lists;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -567,7 +568,7 @@ public void testAddVolumesConcurrently()
 
     // Thread to list all storage available at DataNode,
     // when the volumes are being added in parallel.
-    final Thread listStorageThread = new Thread(new Runnable() {
+    final SubjectInheritingThread listStorageThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         while (addVolumeCompletionLatch.getCount() != newVolumeCount) {
@@ -591,7 +592,7 @@ public void run() {
       public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
         final Random r = new Random();
         Thread addVolThread =
-            new Thread(new Runnable() {
+            new SubjectInheritingThread(new Runnable() {
               @Override
               public void run() {
                 try {
@@ -928,7 +929,7 @@ public void logDelaySendingAckToUpstream(
       final DataNode dataNode = dn;
       final CyclicBarrier reconfigBarrier = new CyclicBarrier(2);
 
-      Thread reconfigThread = new Thread(() -> {
+      Thread reconfigThread = new SubjectInheritingThread(() -> {
         try {
           reconfigBarrier.await();
 
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataNodeVolumeFailure.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataNodeVolumeFailure.java
index f4d66f8c8d0..ef5820bdf9d 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataNodeVolumeFailure.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataNodeVolumeFailure.java
@@ -82,6 +82,7 @@
 import org.apache.hadoop.net.NetUtils;
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -441,7 +442,7 @@ public void delayWhenOfferServiceHoldLock() {
     BPServiceActor actor = service.getBPServiceActors().get(0);
     DatanodeRegistration bpRegistration = actor.getBpRegistration();
 
-    Thread register = new Thread(() -> {
+    Thread register = new SubjectInheritingThread(() -> {
       try {
         service.registrationSucceeded(actor, bpRegistration);
       } catch (IOException e) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataSetLockManager.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataSetLockManager.java
index 81f60200889..02b444df8b8 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataSetLockManager.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataSetLockManager.java
@@ -19,6 +19,7 @@
 
 import org.apache.hadoop.hdfs.server.common.AutoCloseDataSetLock;
 import org.apache.hadoop.hdfs.server.common.DataNodeLockManager.LockLevel;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -85,7 +86,7 @@ public void testBaseFunc() {
   @Test
   @Timeout(value = 5)
   public void testAcquireWriteLockError() throws InterruptedException {
-    Thread t = new Thread(() -> {
+    Thread t = new SubjectInheritingThread(() -> {
       manager.readLock(LockLevel.BLOCK_POOl, "test");
       manager.writeLock(LockLevel.BLOCK_POOl, "test");
     });
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataXceiverBackwardsCompat.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataXceiverBackwardsCompat.java
index c6b5592c3c0..eea64bef1f8 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataXceiverBackwardsCompat.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestDataXceiverBackwardsCompat.java
@@ -34,6 +34,7 @@
 import org.apache.hadoop.net.ServerSocketUtil;
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.util.DataChecksum;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -95,7 +96,7 @@ public NullDataNode(Configuration conf, OutputStream out, int port) throws
           any(StorageType.class), any(String.class), any(ExtendedBlock.class),
           anyBoolean());
 
-      new Thread(new NullServer(port)).start();
+      new SubjectInheritingThread(new NullServer(port)).start();
     }
 
     @Override
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestSimulatedFSDataset.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestSimulatedFSDataset.java
index 200c30a5abb..3dcff8c54aa 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestSimulatedFSDataset.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestSimulatedFSDataset.java
@@ -39,6 +39,7 @@
 import org.apache.hadoop.hdfs.server.datanode.fsdataset.impl.FsDatasetFactory;
 import org.apache.hadoop.hdfs.server.protocol.DatanodeStorage;
 import org.apache.hadoop.util.DataChecksum;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -381,7 +382,7 @@ public void testConcurrentAddBlockPool() throws InterruptedException,
       IOException {
     final String[] bpids = {"BP-TEST1-", "BP-TEST2-"};
     final SimulatedFSDataset fsdataset = new SimulatedFSDataset(null, conf);
-    class AddBlockPoolThread extends Thread {
+    class AddBlockPoolThread extends SubjectInheritingThread {
       private int id;
       private IOException ioe;
       public AddBlockPoolThread(int id) {
@@ -394,7 +395,7 @@ public void test() throws InterruptedException, IOException {
           throw ioe;
         }
       }
-      public void run() {
+      public void work() {
         for (int i=0; i < 10000; i++) {
           // add different block pools concurrently
           String newbpid = bpids[id] + i;
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsDatasetImpl.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsDatasetImpl.java
index 883290ef41c..30a90fac5ce 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsDatasetImpl.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsDatasetImpl.java
@@ -84,6 +84,7 @@
 import org.apache.hadoop.util.FakeTimer;
 import org.apache.hadoop.util.Lists;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
@@ -648,9 +649,9 @@ public void testConcurrentWriteAndDeleteBlock() throws Exception {
     Random random = new Random();
     // Random write block and delete half of them.
     for (int i = 0; i < threadCount; i++) {
-      Thread thread = new Thread() {
+      SubjectInheritingThread thread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           try {
             String bpid = BLOCK_POOL_IDS[random.nextInt(BLOCK_POOL_IDS.length)];
             for (int blockId = 0; blockId < numBlocks; blockId++) {
@@ -931,8 +932,8 @@ public void testRemoveVolumeBeingWritten() throws Exception {
     final CountDownLatch blockReportReceivedLatch = new CountDownLatch(1);
     final CountDownLatch volRemoveStartedLatch = new CountDownLatch(1);
     final CountDownLatch volRemoveCompletedLatch = new CountDownLatch(1);
-    class BlockReportThread extends Thread {
-      public void run() {
+    class BlockReportThread extends SubjectInheritingThread {
+      public void work() {
         // Lets wait for the volume remove process to start
         try {
           volRemoveStartedLatch.await();
@@ -946,8 +947,8 @@ public void run() {
       }
     }
 
-    class ResponderThread extends Thread {
-      public void run() {
+    class ResponderThread extends SubjectInheritingThread {
+      public void work() {
         try (ReplicaHandler replica = dataset
             .createRbw(StorageType.DEFAULT, null, eb, false)) {
           LOG.info("CreateRbw finished");
@@ -973,8 +974,8 @@ public void run() {
       }
     }
 
-    class VolRemoveThread extends Thread {
-      public void run() {
+    class VolRemoveThread extends SubjectInheritingThread {
+      public void work() {
         Set<StorageLocation> volumesToRemove = new HashSet<>();
         try {
           volumesToRemove.add(dataset.getVolume(eb).getStorageLocation());
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsVolumeList.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsVolumeList.java
index 6c00e9690bb..eedca0b5a7c 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsVolumeList.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestFsVolumeList.java
@@ -42,6 +42,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableMap;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -386,9 +387,9 @@ public void testAddRplicaProcessorForAddingReplicaInMap() throws Exception {
     ExecutorService pool = Executors.newFixedThreadPool(10);
     List<Future<?>> futureList = new ArrayList<>();
     for (int i = 0; i < 100; i++) {
-      Thread thread = new Thread() {
+      SubjectInheritingThread thread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           for (int j = 0; j < 10; j++) {
             try {
               DFSTestUtil.createFile(fs, new Path("File_" + getName() + j), 10,
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestLazyPersistFiles.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestLazyPersistFiles.java
index f1f57a9714f..e4110e436f9 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestLazyPersistFiles.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestLazyPersistFiles.java
@@ -23,6 +23,7 @@
 import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsDatasetSpi;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.ThreadUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
@@ -196,7 +197,7 @@ public void run() {
 
     Thread threads[] = new Thread[NUM_TASKS];
     for (int i = 0; i < NUM_TASKS; i++) {
-      threads[i] = new Thread(readerRunnable);
+      threads[i] = new SubjectInheritingThread(readerRunnable);
       threads[i].start();
     }
 
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestReplicaCachingGetSpaceUsed.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestReplicaCachingGetSpaceUsed.java
index c29c2bf1bc8..1c715c755c9 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestReplicaCachingGetSpaceUsed.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestReplicaCachingGetSpaceUsed.java
@@ -31,6 +31,7 @@
 import org.apache.hadoop.hdfs.server.datanode.Replica;
 import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsDatasetSpi;
 import org.apache.hadoop.io.IOUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -175,11 +176,11 @@ public void testFsDatasetImplDeepCopyReplica() {
     modifyThread.setShouldRun(false);
   }
 
-  private class ModifyThread extends Thread {
+  private class ModifyThread extends SubjectInheritingThread {
     private boolean shouldRun = true;
 
     @Override
-    public void run() {
+    public void work() {
       FSDataOutputStream os = null;
       while (shouldRun) {
         try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancer.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancer.java
index fd753386a11..480149a9a56 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancer.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/diskbalancer/TestDiskBalancer.java
@@ -48,6 +48,7 @@
 import org.apache.hadoop.hdfs.server.diskbalancer.planner.NodePlan;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
@@ -56,7 +57,7 @@
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
-import java.util.ArrayList;
+import java.util.ArrayList; 
 import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -717,8 +718,8 @@ public Object answer(InvocationOnMock invocation) {
           getTrimmedStringCollection(DFSConfigKeys.DFS_DATANODE_DATA_DIR_KEY));
       final String newDirs = oldDirs.get(0);
       LOG.info("Reconfigure newDirs:" + newDirs);
-      Thread reconfigThread = new Thread() {
-        public void run() {
+      SubjectInheritingThread reconfigThread = new SubjectInheritingThread() {
+        public void work() {
           try {
             LOG.info("Waiting for work plan creation!");
             createWorkPlanLatch.await();
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/NNThroughputBenchmark.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/NNThroughputBenchmark.java
index ca29433dbd6..c404ed3707b 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/NNThroughputBenchmark.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/NNThroughputBenchmark.java
@@ -85,6 +85,7 @@
 import org.apache.hadoop.util.Tool;
 import org.apache.hadoop.util.ToolRunner;
 import org.apache.hadoop.util.VersionInfo;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.event.Level;
 
 /**
@@ -422,7 +423,7 @@ void printStats() {
   /**
    * One of the threads that perform stats operations.
    */
-  private class StatsDaemon extends Thread {
+  private class StatsDaemon extends SubjectInheritingThread {
     private final int daemonId;
     private int opsPerThread;
     private String arg1;      // argument passed to executeOp()
@@ -438,7 +439,7 @@ private class StatsDaemon extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       localNumOpsExecuted = 0;
       localCumulativeTime = 0;
       arg1 = statsOp.getExecutionArgument(daemonId);
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestAuditLogger.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestAuditLogger.java
index 61c147e6e5e..4afaa049927 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestAuditLogger.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestAuditLogger.java
@@ -39,6 +39,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.test.GenericTestUtils.LogCapturer;
 import org.apache.hadoop.util.Lists;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -315,7 +316,7 @@ public void testAuditLoggerWithCallContext() throws IOException {
           .build();
       CallerContext.setCurrent(context);
       LOG.info("Set current caller context as {}", CallerContext.getCurrent());
-      Thread child = new Thread(new Runnable()
+      Thread child = new SubjectInheritingThread(new Runnable()
       {
         @Override
         public void run() {
@@ -342,7 +343,7 @@ public void run() {
               .setSignature("L".getBytes(CallerContext.SIGNATURE_ENCODING))
               .build();
       LOG.info("Set current caller context as {}", CallerContext.getCurrent());
-      child = new Thread(new Runnable()
+      child = new SubjectInheritingThread(new Runnable()
       {
         @Override
         public void run() {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestCheckpoint.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestCheckpoint.java
index 73a4a52611b..62d1feb67c2 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestCheckpoint.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestCheckpoint.java
@@ -88,6 +88,7 @@
 import org.apache.hadoop.util.ExitUtil.ExitException;
 import org.apache.hadoop.util.Lists;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.event.Level;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -2611,7 +2612,7 @@ private static CheckpointStorage spyOnSecondaryImage(SecondaryNameNode secondary
   /**
    * A utility class to perform a checkpoint in a different thread.
    */
-  private static class DoCheckpointThread extends Thread {
+  private static class DoCheckpointThread extends SubjectInheritingThread {
     private final SecondaryNameNode snn;
     private volatile Throwable thrown = null;
     
@@ -2620,7 +2621,7 @@ private static class DoCheckpointThread extends Thread {
     }
     
     @Override
-    public void run() {
+    public void work() {
       try {
         snn.doCheckpoint();
       } catch (Throwable t) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestDeleteRace.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestDeleteRace.java
index d9002f83948..102420ed265 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestDeleteRace.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestDeleteRace.java
@@ -59,6 +59,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.test.GenericTestUtils.DelayAnswer;
 import org.apache.hadoop.test.Whitebox;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.mockito.Mockito;
@@ -108,7 +109,7 @@ private void testDeleteAddBlockRace(boolean hasSnapshot) throws Exception {
             "/"), "s1");
       }
 
-      Thread deleteThread = new DeleteThread(fs, filePath);
+      SubjectInheritingThread deleteThread = new DeleteThread(fs, filePath);
       deleteThread.start();
 
       try {
@@ -148,7 +149,7 @@ public DatanodeStorageInfo[] chooseTarget(String srcPath,
     }
   }
 
-  private class DeleteThread extends Thread {
+  private class DeleteThread extends SubjectInheritingThread {
     private FileSystem fs;
     private Path path;
 
@@ -158,7 +159,7 @@ private class DeleteThread extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         Thread.sleep(1000);
         LOG.info("Deleting" + path);
@@ -177,7 +178,7 @@ public void run() {
     }
   }
 
-  private class RenameThread extends Thread {
+  private class RenameThread extends SubjectInheritingThread {
     private FileSystem fs;
     private Path from;
     private Path to;
@@ -189,7 +190,7 @@ private class RenameThread extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         Thread.sleep(1000);
         LOG.info("Renaming " + from + " to " + to);
@@ -456,14 +457,14 @@ public void testOpenRenameRace() throws Exception {
       // 6.release writeLock, it's fair lock so open thread gets read lock.
       // 7.open thread unlocks, rename gets write lock and does rename.
       // 8.rename thread unlocks, open thread gets write lock and update time.
-      Thread open = new Thread(() -> {
+      Thread open = new SubjectInheritingThread(() -> {
         try {
           openSem.release();
           fsn.getBlockLocations("foo", src, 0, 5);
         } catch (IOException e) {
         }
       });
-      Thread rename = new Thread(() -> {
+      Thread rename = new SubjectInheritingThread(() -> {
         try {
           openSem.acquire();
           renameSem.release();
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLog.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLog.java
index 84e02d273a8..d7268e95fc7 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLog.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLog.java
@@ -90,6 +90,7 @@
 import org.apache.hadoop.util.Lists;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.log4j.AppenderSkeleton;
 import org.apache.log4j.LogManager;
 import org.apache.log4j.spi.LoggingEvent;
@@ -501,7 +502,7 @@ private void testEditLog(int initialSize) throws IOException {
       for (int i = 0; i < NUM_THREADS; i++) {
         Transactions trans =
           new Transactions(namesystem, NUM_TRANSACTIONS, i*NUM_TRANSACTIONS);
-        threadId[i] = new Thread(trans, "TransactionThread-" + i);
+        threadId[i] = new SubjectInheritingThread(trans, "TransactionThread-" + i);
         threadId[i].start();
       }
   
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLogRace.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLogRace.java
index 8d3effb511f..beda93568e1 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLogRace.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLogRace.java
@@ -67,6 +67,7 @@
 import org.apache.hadoop.ipc.RemoteException;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.mockito.ArgumentMatcher;
 import org.slf4j.event.Level;
 import org.junit.jupiter.api.Test;
@@ -205,7 +206,7 @@ private void startTransactionWorkers(MiniDFSCluster cluster,
     // Create threads and make them run transactions concurrently.
     for (int i = 0; i < NUM_THREADS; i++) {
       Transactions trans = new Transactions(cluster, caughtErr);
-      new Thread(trans, "TransactionThread-" + i).start();
+      new SubjectInheritingThread(trans, "TransactionThread-" + i).start();
       workers.add(trans);
     }
   }
@@ -425,9 +426,9 @@ public void testSaveImageWhileSyncInProgress() throws Exception {
           new AtomicReference<Throwable>();
       final CountDownLatch waitToEnterFlush = new CountDownLatch(1);
       
-      final Thread doAnEditThread = new Thread() {
+      final SubjectInheritingThread doAnEditThread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           try {
             LOG.info("Starting mkdirs");
             namesystem.mkdirs("/test",
@@ -518,9 +519,9 @@ public void testSaveRightBeforeSync() throws Exception {
           new AtomicReference<Throwable>();
       final CountDownLatch sleepingBeforeSync = new CountDownLatch(1);
 
-      final Thread doAnEditThread = new Thread() {
+      final SubjectInheritingThread doAnEditThread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           try {
             LOG.info("Starting setOwner");
             namesystem.writeLock(RwLockMode.FS);
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFSNamesystemLock.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFSNamesystemLock.java
index 0ffe35ca3e1..261c297fcf6 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFSNamesystemLock.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFSNamesystemLock.java
@@ -29,6 +29,7 @@
 import org.apache.hadoop.test.MetricsAsserts;
 import org.apache.hadoop.util.FakeTimer;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
@@ -268,9 +269,9 @@ public void testFSReadLockLongHoldingReport() throws Exception {
     // Track but do not Report if it's held for a long time when re-entering
     // read lock but time since last report does not exceed the suppress
     // warning interval
-    Thread tLong = new Thread() {
+    SubjectInheritingThread tLong = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         fsnLock.readLock();
         // Add one lock hold which is the longest, but occurs under a different
         // stack trace, to ensure this is the one that gets logged
@@ -298,7 +299,7 @@ public void run() {
     fsnLock.readUnlock();
     // Assert that stack trace eventually logged is the one for the longest hold
     String stackTracePatternString =
-        String.format("INFO.+%s(.+\n){5}\\Q%%s\\E\\.run", readLockLogStmt);
+        String.format("INFO.+%s(.+\n){5}\\Q%%s\\E\\.work", readLockLogStmt);
     Pattern tLongPattern = Pattern.compile(
         String.format(stackTracePatternString, tLong.getClass().getName()));
     assertTrue(tLongPattern.matcher(logs.getOutput()).find());
@@ -318,9 +319,9 @@ public void run() {
     logs.clearOutput();
     final CountDownLatch barrier = new CountDownLatch(1);
     final CountDownLatch barrier2 = new CountDownLatch(1);
-    Thread t1 = new Thread() {
+    SubjectInheritingThread t1 = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           fsnLock.readLock();
           timer.advance(readLockReportingThreshold + 1);
@@ -332,9 +333,9 @@ public void run() {
         }
       }
     };
-    Thread t2 = new Thread() {
+    SubjectInheritingThread t2 = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work () {
         try {
           barrier.await(); // Wait until t1 finishes sleeping
           fsnLock.readLock();
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFSNamesystemMBean.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFSNamesystemMBean.java
index f18ff3020aa..68f9ca3b525 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFSNamesystemMBean.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFSNamesystemMBean.java
@@ -41,6 +41,7 @@
 import org.apache.hadoop.metrics2.impl.ConfigBuilder;
 import org.apache.hadoop.metrics2.impl.TestMetricsConfig;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.eclipse.jetty.util.ajax.JSON;
@@ -55,10 +56,10 @@ public class TestFSNamesystemMBean {
    * JMX properties. If it can access all the properties, the test is
    * considered successful.
    */
-  private static class MBeanClient extends Thread {
+  private static class MBeanClient extends SubjectInheritingThread {
     private boolean succeeded = false;
     @Override
-    public void run() {
+    public void work() {
       try {
         MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
 
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFileTruncate.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFileTruncate.java
index 6e7ed2e82f4..4b507035fa6 100755
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFileTruncate.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestFileTruncate.java
@@ -68,6 +68,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Time;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.event.Level;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -255,7 +256,7 @@ public void delay() {
     DataNodeFaultInjector.set(injector);
 
     // Truncate by using different client name.
-    Thread t = new Thread(() -> {
+    Thread t = new SubjectInheritingThread(() -> {
       String hdfsCacheDisableKey = "fs.hdfs.impl.disable.cache";
       boolean originCacheDisable =
           conf.getBoolean(hdfsCacheDisableKey, false);
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestLargeDirectoryDelete.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestLargeDirectoryDelete.java
index 2dffeaee5bd..3a7a882b8bd 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestLargeDirectoryDelete.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestLargeDirectoryDelete.java
@@ -32,6 +32,7 @@
 import org.apache.hadoop.hdfs.HdfsConfiguration;
 import org.apache.hadoop.hdfs.MiniDFSCluster;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -165,12 +166,12 @@ protected void execute() throws Throwable {
    * implementation class, the thread is notified: other threads can wait
    * for it to terminate
    */
-  private abstract class TestThread extends Thread {
+  private abstract class TestThread extends SubjectInheritingThread {
     volatile Throwable thrown;
     protected volatile boolean live = true;
 
     @Override
-    public void run() {
+    public void work() {
       try {
         execute();
       } catch (Throwable throwable) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestListOpenFiles.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestListOpenFiles.java
index 6c6c2b0008d..5f5bc122ba9 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestListOpenFiles.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestListOpenFiles.java
@@ -56,6 +56,7 @@
 import org.apache.hadoop.hdfs.MiniDFSCluster;
 import org.apache.hadoop.hdfs.tools.DFSAdmin;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.util.ChunkedArrayList;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -222,7 +223,7 @@ public void testListOpenFilesInHA() throws Exception {
       final AtomicBoolean failoverCompleted = new AtomicBoolean(false);
       final AtomicBoolean listOpenFilesError = new AtomicBoolean(false);
       final int listingIntervalMsec = 250;
-      Thread clientThread = new Thread(new Runnable() {
+      Thread clientThread = new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           while(!failoverCompleted.get()) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestMetaSave.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestMetaSave.java
index 4135cb354e7..0260cee687e 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestMetaSave.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestMetaSave.java
@@ -44,6 +44,7 @@
 import org.apache.hadoop.hdfs.HdfsConfiguration;
 import org.apache.hadoop.hdfs.MiniDFSCluster;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.MethodOrderer;
@@ -222,7 +223,7 @@ public void testMetaSaveOverwrite() throws Exception {
     }
   }
 
-  class MetaSaveThread extends Thread {
+  class MetaSaveThread extends SubjectInheritingThread {
     NamenodeProtocols nnRpc;
     String filename;
     public MetaSaveThread(NamenodeProtocols nnRpc, String filename) {
@@ -231,7 +232,7 @@ public MetaSaveThread(NamenodeProtocols nnRpc, String filename) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         nnRpc.metaSave(filename);
       } catch (IOException e) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestReencryptionHandler.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestReencryptionHandler.java
index a27e4d8676e..6e104973529 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestReencryptionHandler.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestReencryptionHandler.java
@@ -27,6 +27,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.KMSUtil;
 import org.apache.hadoop.util.StopWatch;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.test.Whitebox;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -177,8 +178,8 @@ public void testThrottleAccumulatingTasks() throws Exception {
       zst.addTask(mock);
     }
 
-    Thread removeTaskThread = new Thread() {
-      public void run() {
+    SubjectInheritingThread removeTaskThread = new SubjectInheritingThread() {
+      public void work() {
         try {
           Thread.sleep(3000);
         } catch (InterruptedException ie) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestSecurityTokenEditLog.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestSecurityTokenEditLog.java
index 368a9a84600..6b6dcfe9da0 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestSecurityTokenEditLog.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestSecurityTokenEditLog.java
@@ -38,6 +38,7 @@
 import org.apache.hadoop.io.Text;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.mockito.invocation.InvocationOnMock;
@@ -136,7 +137,7 @@ public void testEditLog() throws IOException {
       Thread threadId[] = new Thread[NUM_THREADS];
       for (int i = 0; i < NUM_THREADS; i++) {
         Transactions trans = new Transactions(namesystem, NUM_TRANSACTIONS);
-        threadId[i] = new Thread(trans, "TransactionThread-" + i);
+        threadId[i] = new SubjectInheritingThread(trans, "TransactionThread-" + i);
         threadId[i].start();
       }
   
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestBootstrapStandby.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestBootstrapStandby.java
index 6fcaffaa6ae..00e56a779fb 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestBootstrapStandby.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestBootstrapStandby.java
@@ -63,6 +63,7 @@
 import org.junit.jupiter.api.Timeout;
 
 import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableList;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 public class TestBootstrapStandby {
   private static final Logger LOG =
@@ -409,7 +410,7 @@ public void testRateThrottling() throws Exception {
     final int timeOut = (int)(imageFile.length() / minXferRatePerMS) + 1;
     // A very low DFS_IMAGE_TRANSFER_RATE_KEY value won't affect bootstrapping
     final AtomicBoolean bootStrapped = new AtomicBoolean(false);
-    new Thread(
+    new SubjectInheritingThread(
         new Runnable() {
           @Override
           public void run() {
@@ -439,7 +440,7 @@ public Boolean get() {
     // A very low DFS_IMAGE_TRANSFER_BOOTSTRAP_STANDBY_RATE_KEY value should
     // cause timeout
     bootStrapped.set(false);
-    new Thread(
+    new SubjectInheritingThread(
         new Runnable() {
           @Override
           public void run() {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestConsistentReadsObserver.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestConsistentReadsObserver.java
index 09d718fb012..e4c59433780 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestConsistentReadsObserver.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestConsistentReadsObserver.java
@@ -56,6 +56,7 @@
 import org.apache.hadoop.metrics2.MetricsRecordBuilder;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeEach;
@@ -152,7 +153,7 @@ public void testMsyncSimple() throws Exception {
     dfs.mkdir(testPath, FsPermission.getDefault());
     assertSentTo(0);
 
-    Thread reader = new Thread(() -> {
+    Thread reader = new SubjectInheritingThread(() -> {
       try {
         // this read will block until roll and tail edits happen.
         dfs.getFileStatus(testPath);
@@ -202,7 +203,7 @@ private void testMsync(boolean autoMsync, long autoMsyncPeriodMs)
     dfs.mkdir(testPath, FsPermission.getDefault());
     assertSentTo(0);
 
-    Thread reader = new Thread(() -> {
+    Thread reader = new SubjectInheritingThread(() -> {
       try {
         // After msync, client should have the latest state ID from active.
         // Therefore, the subsequent getFileStatus call should succeed.
@@ -293,7 +294,7 @@ public void testCallFromNewClient() throws Exception {
           (DistributedFileSystem) FileSystem.get(conf2);
       dfs2.getClient().getHAServiceState();
 
-      Thread reader = new Thread(() -> {
+      Thread reader = new SubjectInheritingThread(() -> {
         try {
           dfs2.getFileStatus(testPath);
           readStatus.set(1);
@@ -334,7 +335,7 @@ public void testUncoordinatedCall() throws Exception {
     AtomicInteger readStatus = new AtomicInteger(0);
 
     // create a separate thread to make a blocking read.
-    Thread reader = new Thread(() -> {
+    Thread reader = new SubjectInheritingThread(() -> {
       try {
         // this read call will block until server state catches up. But due to
         // configuration, this will take a very long time.
@@ -439,7 +440,7 @@ public void testRpcQueueTimeNumOpsMetrics() throws Exception {
     dfs.mkdir(testPath, FsPermission.getDefault());
     assertSentTo(0);
 
-    Thread reader = new Thread(new Runnable() {
+    Thread reader = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestDelegationTokensWithHA.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestDelegationTokensWithHA.java
index 9006bb47c3d..756094e19fa 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestDelegationTokensWithHA.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestDelegationTokensWithHA.java
@@ -18,6 +18,7 @@
 package org.apache.hadoop.hdfs.server.namenode.ha;
 
 import org.apache.hadoop.thirdparty.com.google.common.base.Joiner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.conf.Configuration;
@@ -268,9 +269,9 @@ public void testDelegationTokenDuringNNFailover() throws Exception {
           HAServiceState.STANDBY.toString(), e);
     }
     
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           cluster.transitionToActive(1);
         } catch (Exception e) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestHASafeMode.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestHASafeMode.java
index 2a011469d58..44f20bd8265 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestHASafeMode.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestHASafeMode.java
@@ -68,6 +68,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.test.Whitebox;
 import org.apache.hadoop.util.Lists;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -142,9 +143,9 @@ public void testClientRetrySafeMode() throws Exception {
         .getBlockManager());
     assertTrue(nn0.getNamesystem().isInStartupSafeMode());
     LOG.info("enter safemode");
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           boolean mkdir = fs.mkdirs(test);
           LOG.info("mkdir finished, result is " + mkdir);
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRetryCacheWithHA.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRetryCacheWithHA.java
index e2857a4d84d..f679bcc148b 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRetryCacheWithHA.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestRetryCacheWithHA.java
@@ -90,6 +90,7 @@
 import org.apache.hadoop.io.retry.RetryPolicy;
 import org.apache.hadoop.ipc.RetryCache.CacheEntry;
 import org.apache.hadoop.util.LightWeightCache;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -1330,9 +1331,9 @@ public void testClientRetryWithFailover(final AtMostOnceOp op)
     // set DummyRetryInvocationHandler#block to true
     DummyRetryInvocationHandler.block.set(true);
     
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           op.invoke();
           Object result = op.getResult();
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestStandbyCheckpoints.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestStandbyCheckpoints.java
index 6f36d5e19bc..e010193bf3e 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestStandbyCheckpoints.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestStandbyCheckpoints.java
@@ -53,6 +53,7 @@
 import org.apache.hadoop.test.PathUtils;
 import org.apache.hadoop.util.Lists;
 import org.apache.hadoop.util.ThreadUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.log4j.spi.LoggingEvent;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -559,9 +560,9 @@ public void testReadsAllowedDuringCheckpoint() throws Exception {
     ThreadUtil.sleepAtLeastIgnoreInterrupts(1000);
     
     // Perform an RPC that needs to take the write lock.
-    Thread t = new Thread() {
+    SubjectInheritingThread t = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           nns[1].getRpcServer().restoreFailedStorage("false");
         } catch (IOException e) {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/snapshot/TestOpenFilesWithSnapshot.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/snapshot/TestOpenFilesWithSnapshot.java
index 6a62d16d399..f4d141b3877 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/snapshot/TestOpenFilesWithSnapshot.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/snapshot/TestOpenFilesWithSnapshot.java
@@ -44,6 +44,7 @@
 import org.apache.hadoop.hdfs.server.namenode.NameNodeAdapter;
 import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -709,7 +710,7 @@ public void testOpenFileWritingAcrossSnapDeletion() throws Exception {
     final AtomicBoolean writerError = new AtomicBoolean(false);
     final CountDownLatch startLatch = new CountDownLatch(1);
     final CountDownLatch deleteLatch = new CountDownLatch(1);
-    Thread t = new Thread(new Runnable() {
+    Thread t = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/shortcircuit/TestShortCircuitLocalRead.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/shortcircuit/TestShortCircuitLocalRead.java
index 19fed937ebb..0b725b93d56 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/shortcircuit/TestShortCircuitLocalRead.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/shortcircuit/TestShortCircuitLocalRead.java
@@ -62,6 +62,7 @@
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.BeforeAll;
@@ -563,11 +564,11 @@ public static void main(String[] args) throws Exception {
 
     long start = Time.now();
     final int iteration = 20;
-    Thread[] threads = new Thread[threadCount];
+    SubjectInheritingThread[] threads = new SubjectInheritingThread[threadCount];
     for (int i = 0; i < threadCount; i++) {
-      threads[i] = new Thread() {
+      threads[i] = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           for (int i = 0; i < iteration; i++) {
             try {
               String user = getCurrentUser();
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestReferenceCountMap.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestReferenceCountMap.java
index 598d18e9f0b..c3296ee7b4a 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestReferenceCountMap.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/util/TestReferenceCountMap.java
@@ -19,6 +19,7 @@
 package org.apache.hadoop.hdfs.util;
 
 import org.apache.hadoop.hdfs.server.namenode.AclFeature;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -85,13 +86,13 @@ public void testRefCountMapConcurrently() throws Exception {
     assertEquals(LOOP_COUNTER, countMap.getReferenceCount(aclFeature2));
   }
 
-  class PutThread extends Thread {
+  class PutThread extends SubjectInheritingThread {
     private ReferenceCountMap<AclFeature> referenceCountMap;
     PutThread(ReferenceCountMap<AclFeature> referenceCountMap) {
       this.referenceCountMap = referenceCountMap;
     }
     @Override
-    public void run() {
+    public void work() {
       for (int i = 0; i < LOOP_COUNTER; i++) {
         referenceCountMap.put(aclFeature1);
         referenceCountMap.put(aclFeature2);
@@ -99,13 +100,13 @@ public void run() {
     }
   };
 
-  class RemoveThread extends Thread {
+  class RemoveThread extends SubjectInheritingThread {
     private ReferenceCountMap<AclFeature> referenceCountMap;
     RemoveThread(ReferenceCountMap<AclFeature> referenceCountMap) {
       this.referenceCountMap = referenceCountMap;
     }
     @Override
-    public void run() {
+    public void work() {
       for (int i = 0; i < LOOP_COUNTER; i++) {
         referenceCountMap.remove(aclFeature1);
         referenceCountMap.remove(aclFeature2);
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHDFSForHA.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHDFSForHA.java
index 922876c598d..48751695e48 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHDFSForHA.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHDFSForHA.java
@@ -57,6 +57,7 @@
 import org.apache.hadoop.security.token.SecretManager;
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.test.Whitebox;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.eclipse.jetty.util.ajax.JSON;
@@ -296,9 +297,9 @@ public void testRetryWhileNNStartup() throws Exception {
       final NamenodeProtocols rpcServer = namenode.getRpcServer();
       Whitebox.setInternalState(namenode, "rpcServer", null);
 
-      new Thread() {
+      new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           boolean result = false;
           FileSystem fs = null;
           try {
diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHdfsTimeouts.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHdfsTimeouts.java
index 5924a8dedce..d11dc5ec2be 100644
--- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHdfsTimeouts.java
+++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/web/TestWebHdfsTimeouts.java
@@ -51,6 +51,7 @@
 import org.apache.hadoop.net.NetUtils;
 import org.apache.hadoop.security.authentication.client.ConnectionConfigurator;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Timeout;
 import org.opentest4j.TestAbortedException;
@@ -325,9 +326,9 @@ public void testTwoStepWriteReadTimeout(TimeoutSource src) throws Exception {
   private void startSingleTemporaryRedirectResponseThread(
       final boolean consumeConnectionBacklog) {
     fs.connectionFactory = URLConnectionFactory.DEFAULT_SYSTEM_CONNECTION_FACTORY;
-    serverThread = new Thread() {
+    serverThread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         Socket clientSocket = null;
         OutputStream out = null;
         InputStream in = null;
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/LocalContainerLauncher.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/LocalContainerLauncher.java
index 4c245136efb..1548bcc3c6b 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/LocalContainerLauncher.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapred/LocalContainerLauncher.java
@@ -64,6 +64,7 @@
 import org.apache.hadoop.util.ShutdownHookManager;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.concurrent.HadoopExecutors;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationConstants.Environment;
 import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
 
@@ -151,7 +152,7 @@ public void serviceStart() throws Exception {
         HadoopExecutors.newSingleThreadExecutor(new ThreadFactoryBuilder().
             setDaemon(true).setNameFormat("uber-SubtaskRunner").build());
     // create and start an event handling thread
-    eventHandler = new Thread(new EventHandler(), "uber-EventHandler");
+    eventHandler = new SubjectInheritingThread(new EventHandler(), "uber-EventHandler");
     // if the job classloader is specified, set it onto the event handler as the
     // thread context classloader so that it can be used by the event handler
     // as well as the subtask runner threads
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryEventHandler.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryEventHandler.java
index 6ab06696c19..16f30c90177 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryEventHandler.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/jobhistory/JobHistoryEventHandler.java
@@ -65,6 +65,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.timeline.TimelineEntity;
 import org.apache.hadoop.yarn.api.records.timeline.TimelineEvent;
 import org.apache.hadoop.yarn.api.records.timeline.TimelinePutResponse;
@@ -351,7 +352,7 @@ protected void serviceStart() throws Exception {
     } else if (timelineV2Client != null) {
       timelineV2Client.start();
     }
-    eventHandlingThread = new Thread(new Runnable() {
+    eventHandlingThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         JobHistoryEvent event = null;
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/MRAppMaster.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/MRAppMaster.java
index eb3583b41bc..703f0b1f587 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/MRAppMaster.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/MRAppMaster.java
@@ -134,6 +134,7 @@
 import org.apache.hadoop.util.ReflectionUtils;
 import org.apache.hadoop.util.ShutdownHookManager;
 import org.apache.hadoop.util.StringInterner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.YarnUncaughtExceptionHandler;
 import org.apache.hadoop.yarn.api.ApplicationConstants;
 import org.apache.hadoop.yarn.api.ApplicationConstants.Environment;
@@ -739,10 +740,10 @@ private class JobFinishEventHandler implements EventHandler<JobFinishEvent> {
     public void handle(JobFinishEvent event) {
       // Create a new thread to shutdown the AM. We should not do it in-line
       // to avoid blocking the dispatcher itself.
-      new Thread() {
+      new SubjectInheritingThread() {
         
         @Override
-        public void run() {
+        public void work() {
           shutDownJob();
         }
       }.start();
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/TaskHeartbeatHandler.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/TaskHeartbeatHandler.java
index 13389d67efb..194f844bd71 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/TaskHeartbeatHandler.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/TaskHeartbeatHandler.java
@@ -33,6 +33,7 @@
 import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptEvent;
 import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptEventType;
 import org.apache.hadoop.service.AbstractService;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.event.EventHandler;
 import org.apache.hadoop.yarn.util.Clock;
 import org.slf4j.Logger;
@@ -125,7 +126,7 @@ protected void serviceInit(Configuration conf) throws Exception {
 
   @Override
   protected void serviceStart() throws Exception {
-    lostTaskCheckerThread = new Thread(new PingChecker());
+    lostTaskCheckerThread = new SubjectInheritingThread(new PingChecker());
     lostTaskCheckerThread.setName("TaskHeartbeatHandler PingChecker");
     lostTaskCheckerThread.start();
     super.serviceStart();
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/commit/CommitterEventHandler.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/commit/CommitterEventHandler.java
index c72e13e648e..5e444eb1386 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/commit/CommitterEventHandler.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/commit/CommitterEventHandler.java
@@ -47,6 +47,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.util.concurrent.HadoopThreadPoolExecutor;
 import org.apache.hadoop.yarn.event.EventHandler;
 import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
@@ -126,7 +127,7 @@ protected void serviceStart() throws Exception {
       ThreadFactory backingTf = new ThreadFactory() {
         @Override
         public Thread newThread(Runnable r) {
-          Thread thread = new Thread(r);
+          Thread thread = new SubjectInheritingThread(r);
           thread.setContextClassLoader(jobClassLoader);
           return thread;
         }
@@ -136,7 +137,7 @@ public Thread newThread(Runnable r) {
     ThreadFactory tf = tfBuilder.build();
     launcherPool = new HadoopThreadPoolExecutor(5, 5, 1,
         TimeUnit.HOURS, new LinkedBlockingQueue<Runnable>(), tf);
-    eventHandlingThread = new Thread(new Runnable() {
+    eventHandlingThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         CommitterEvent event = null;
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/launcher/ContainerLauncherImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/launcher/ContainerLauncherImpl.java
index d184d9be64b..ee7fbde9939 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/launcher/ContainerLauncherImpl.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/launcher/ContainerLauncherImpl.java
@@ -43,6 +43,7 @@
 import org.apache.hadoop.mapreduce.v2.app.job.event.TaskAttemptEventType;
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.util.concurrent.HadoopThreadPoolExecutor;
 import org.apache.hadoop.yarn.api.protocolrecords.SignalContainerRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.StartContainerRequest;
@@ -285,9 +286,9 @@ protected void serviceStart() throws Exception {
         Integer.MAX_VALUE, 1, TimeUnit.HOURS,
         new LinkedBlockingQueue<Runnable>(),
         tf);
-    eventHandlingThread = new Thread() {
+    eventHandlingThread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         ContainerLauncherEvent event = null;
         Set<String> allNodes = new HashSet<String>();
 
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java
index b836120a8dc..ec5561f0ecf 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMCommunicator.java
@@ -40,6 +40,7 @@
 import org.apache.hadoop.mapreduce.v2.util.MRWebAppUtil;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.service.AbstractService;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationMasterProtocol;
 import org.apache.hadoop.yarn.api.protocolrecords.FinishApplicationMasterRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.FinishApplicationMasterResponse;
@@ -300,7 +301,7 @@ public void run() {
   }
 
   protected void startAllocatorThread() {
-    allocatorThread = new Thread(new AllocatorRunnable());
+    allocatorThread = new SubjectInheritingThread(new AllocatorRunnable());
     allocatorThread.setName("RMCommunicator Allocator");
     allocatorThread.start();
   }
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java
index cf2f90ff1e5..a0f73b0b4ff 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/rm/RMContainerAllocator.java
@@ -62,6 +62,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.util.StringInterner;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
 import org.apache.hadoop.yarn.api.records.Container;
 import org.apache.hadoop.yarn.api.records.ContainerExitStatus;
@@ -111,7 +112,7 @@ public class RMContainerAllocator extends RMContainerRequestor
   public static final String RAMPDOWN_DIAGNOSTIC = "Reducer preempted "
       + "to make room for pending map attempts";
 
-  private Thread eventHandlingThread;
+  private SubjectInheritingThread eventHandlingThread;
   private final AtomicBoolean stopped;
 
   static {
@@ -246,10 +247,10 @@ protected void serviceInit(Configuration conf) throws Exception {
 
   @Override
   protected void serviceStart() throws Exception {
-    this.eventHandlingThread = new Thread() {
+    this.eventHandlingThread = new SubjectInheritingThread() {
       @SuppressWarnings("unchecked")
       @Override
-      public void run() {
+      public void work() {
 
         ContainerAllocatorEvent event;
 
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/speculate/DefaultSpeculator.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/speculate/DefaultSpeculator.java
index 800ff180970..7eb786c5e50 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/speculate/DefaultSpeculator.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/main/java/org/apache/hadoop/mapreduce/v2/app/speculate/DefaultSpeculator.java
@@ -45,6 +45,7 @@
 import org.apache.hadoop.mapreduce.v2.app.job.event.TaskEvent;
 import org.apache.hadoop.mapreduce.v2.app.job.event.TaskEventType;
 import org.apache.hadoop.service.AbstractService;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.event.EventHandler;
 import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
 import org.apache.hadoop.yarn.util.Clock;
@@ -219,7 +220,7 @@ public void run() {
               }
             }
           };
-    speculationBackgroundThread = new Thread
+    speculationBackgroundThread = new SubjectInheritingThread
         (speculationBackgroundCore, "DefaultSpeculator background processing");
     speculationBackgroundThread.start();
 
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRAppBenchmark.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRAppBenchmark.java
index 34f4c8c7164..3288390e62d 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRAppBenchmark.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/MRAppBenchmark.java
@@ -39,6 +39,7 @@
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationMasterProtocol;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
@@ -137,7 +138,7 @@ public void handle(ContainerAllocatorEvent event) {
       }
       @Override
       protected void serviceStart() throws Exception {
-        thread = new Thread(new Runnable() {
+        thread = new SubjectInheritingThread(new Runnable() {
           @Override
           @SuppressWarnings("unchecked")
           public void run() {
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/local/TestLocalContainerAllocator.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/local/TestLocalContainerAllocator.java
index eef1a4a1083..e0d2a323ee6 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/local/TestLocalContainerAllocator.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-app/src/test/java/org/apache/hadoop/mapreduce/v2/app/local/TestLocalContainerAllocator.java
@@ -48,6 +48,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.security.token.TokenIdentifier;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationMasterProtocol;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
@@ -237,7 +238,7 @@ protected void unregister() {
 
     @Override
     protected void startAllocatorThread() {
-      allocatorThread = new Thread();
+      allocatorThread = new SubjectInheritingThread();
     }
 
     @Override
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapred/LocalJobRunner.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapred/LocalJobRunner.java
index aae1fd0b673..c0e149b1348 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapred/LocalJobRunner.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapred/LocalJobRunner.java
@@ -72,6 +72,7 @@
 
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
 import org.apache.hadoop.util.concurrent.HadoopExecutors;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -114,7 +115,7 @@ public ProtocolSignature getProtocolSignature(String protocol,
         this, protocol, clientVersion, clientMethodsHash);
   }
 
-  private class Job extends Thread implements TaskUmbilicalProtocol {
+  private class Job extends SubjectInheritingThread implements TaskUmbilicalProtocol {
     // The job directory on the system: JobClient places job configurations here.
     // This is analogous to JobTracker's system directory.
     private Path systemJobDir;
@@ -521,7 +522,7 @@ private void runTasks(List<RunnableWithThrowable> runnables,
     }
 
     @Override
-    public void run() {
+    public void work() {
       JobID jobId = profile.getJobID();
       JobContext jContext = new JobContextImpl(job, jobId);
       
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/CleanupQueue.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/CleanupQueue.java
index a40a40ada02..4c217a4c7a0 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/CleanupQueue.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/CleanupQueue.java
@@ -26,6 +26,7 @@
 
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 class CleanupQueue {
 
@@ -100,7 +101,7 @@ protected boolean isQueueEmpty() {
     return (cleanupThread.queue.size() == 0);
   }
 
-  private static class PathCleanupThread extends Thread {
+  private static class PathCleanupThread extends SubjectInheritingThread {
 
     // cleanup queue which deletes files/directories of the paths queued up.
     private LinkedBlockingQueue<PathDeletionContext> queue =
@@ -120,7 +121,7 @@ void addToQueue(PathDeletionContext[] contexts) {
       }
     }
 
-    public void run() {
+    public void work() {
       if (LOG.isDebugEnabled()) {
         LOG.debug(getName() + " started.");
       }
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/MapTask.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/MapTask.java
index 4f86f912838..f41c94bf4b5 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/MapTask.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/MapTask.java
@@ -74,6 +74,7 @@
 import org.apache.hadoop.util.ReflectionUtils;
 import org.apache.hadoop.util.StringInterner;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -1549,10 +1550,10 @@ public void flush() throws IOException, ClassNotFoundException,
 
     public void close() { }
 
-    protected class SpillThread extends Thread {
+    protected class SpillThread extends SubjectInheritingThread {
 
       @Override
-      public void run() {
+      public void work() {
         spillLock.lock();
         spillThreadRunning = true;
         try {
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/Task.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/Task.java
index 6861f1b2cd3..44a4b41ef85 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/Task.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/Task.java
@@ -72,6 +72,7 @@
 import org.apache.hadoop.util.ShutdownHookManager;
 import org.apache.hadoop.util.StringInterner;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -952,7 +953,7 @@ void resetDoneFlag() {
     }
     public void startCommunicationThread() {
       if (pingThread == null) {
-        pingThread = new Thread(this, "communication thread");
+        pingThread = new SubjectInheritingThread(this, "communication thread");
         pingThread.setDaemon(true);
         pingThread.start();
       }
@@ -963,7 +964,7 @@ public void startDiskLimitCheckerThreadIfNeeded() {
           MRJobConfig.JOB_SINGLE_DISK_LIMIT_BYTES,
           MRJobConfig.DEFAULT_JOB_SINGLE_DISK_LIMIT_BYTES) >= 0) {
         try {
-          diskLimitCheckThread = new Thread(new DiskLimitCheck(conf),
+          diskLimitCheckThread = new SubjectInheritingThread(new DiskLimitCheck(conf),
               "disk limit check thread");
           diskLimitCheckThread.setDaemon(true);
           diskLimitCheckThread.start();
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/pipes/Application.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/pipes/Application.java
index 15b0961e57d..68ae9e97f19 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/pipes/Application.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/pipes/Application.java
@@ -57,6 +57,7 @@
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.util.ReflectionUtils;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -280,7 +281,7 @@ public static String createDigest(byte[] password, String data)
   }
 
   @VisibleForTesting
-  public static class PingSocketCleaner extends Thread {
+  public static class PingSocketCleaner extends SubjectInheritingThread {
     private final ServerSocket serverSocket;
     private final int soTimeout;
 
@@ -291,7 +292,7 @@ public static class PingSocketCleaner extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       LOG.info("PingSocketCleaner started...");
       while (!Thread.currentThread().isInterrupted()) {
         Socket clientSocket = null;
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/pipes/BinaryProtocol.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/pipes/BinaryProtocol.java
index 89c594a89b0..fdfe0776874 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/pipes/BinaryProtocol.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/pipes/BinaryProtocol.java
@@ -42,6 +42,7 @@
 import org.apache.hadoop.mapred.InputSplit;
 import org.apache.hadoop.mapred.JobConf;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -95,7 +96,7 @@ private enum MessageType { START(0),
 
   private static class UplinkReaderThread<K2 extends WritableComparable,
                                           V2 extends Writable>  
-    extends Thread {
+    extends SubjectInheritingThread {
     
     private DataInputStream inStream;
     private UpwardProtocol<K2, V2> handler;
@@ -117,7 +118,7 @@ public void closeConnection() throws IOException {
       inStream.close();
     }
 
-    public void run() {
+    public void work() {
       while (true) {
         try {
           if (Thread.currentThread().isInterrupted()) {
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/chain/Chain.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/chain/Chain.java
index 803ece7480c..61521765d61 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/chain/Chain.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/chain/Chain.java
@@ -40,6 +40,7 @@
 import org.apache.hadoop.mapreduce.lib.map.WrappedMapper;
 import org.apache.hadoop.mapreduce.lib.reduce.WrappedReducer;
 import org.apache.hadoop.util.ReflectionUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 /**
  * The Chain class provides all the common functionality for the
@@ -296,7 +297,7 @@ private synchronized boolean setIfUnsetThrowable(Throwable th) {
     return false;
   }
 
-  private class MapRunner<KEYIN, VALUEIN, KEYOUT, VALUEOUT> extends Thread {
+  private class MapRunner<KEYIN, VALUEIN, KEYOUT, VALUEOUT> extends SubjectInheritingThread {
     private Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> mapper;
     private Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>.Context chainContext;
     private RecordReader<KEYIN, VALUEIN> rr;
@@ -313,7 +314,7 @@ public MapRunner(Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> mapper,
     }
 
     @Override
-    public void run() {
+    public void work() {
       if (getThrowable() != null) {
         return;
       }
@@ -329,7 +330,7 @@ public void run() {
     }
   }
 
-  private class ReduceRunner<KEYIN, VALUEIN, KEYOUT, VALUEOUT> extends Thread {
+  private class ReduceRunner<KEYIN, VALUEIN, KEYOUT, VALUEOUT> extends SubjectInheritingThread {
     private Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT> reducer;
     private Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT>.Context chainContext;
     private RecordWriter<KEYOUT, VALUEOUT> rw;
@@ -344,7 +345,7 @@ private class ReduceRunner<KEYIN, VALUEIN, KEYOUT, VALUEOUT> extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         reducer.run(chainContext);
         rw.close(chainContext);
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/map/MultithreadedMapper.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/map/MultithreadedMapper.java
index 382ed959f12..cb48a0e4a34 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/map/MultithreadedMapper.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/map/MultithreadedMapper.java
@@ -19,6 +19,7 @@
 package org.apache.hadoop.mapreduce.lib.map;
 
 import org.apache.hadoop.util.ReflectionUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
 import org.apache.hadoop.conf.Configuration;
@@ -247,7 +248,7 @@ public float getProgress() {
     }
   }
 
-  private class MapRunner extends Thread {
+  private class MapRunner extends SubjectInheritingThread {
     private Mapper<K1,V1,K2,V2> mapper;
     private Context subcontext;
     private Throwable throwable;
@@ -269,7 +270,7 @@ private class MapRunner extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         mapper.run(subcontext);
         reader.close();
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/EventFetcher.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/EventFetcher.java
index 0e28c3b41c0..18574067446 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/EventFetcher.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/EventFetcher.java
@@ -23,10 +23,11 @@
 import org.apache.hadoop.mapred.TaskCompletionEvent;
 import org.apache.hadoop.mapred.TaskUmbilicalProtocol;
 import org.apache.hadoop.mapreduce.TaskAttemptID;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-class EventFetcher<K,V> extends Thread {
+class EventFetcher<K,V> extends SubjectInheritingThread {
   private static final long SLEEP_TIME = 1000;
   private static final int MAX_RETRIES = 10;
   private static final int RETRY_PERIOD = 5000;
@@ -56,7 +57,7 @@ public EventFetcher(TaskAttemptID reduce,
   }
 
   @Override
-  public void run() {
+  public void work() {
     int failures = 0;
     LOG.info(reduce + " Thread started: " + getName());
     
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/Fetcher.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/Fetcher.java
index 59ef95bdd46..da598e807e2 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/Fetcher.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/Fetcher.java
@@ -48,6 +48,7 @@
 import org.apache.hadoop.mapreduce.CryptoUtils;
 import org.apache.hadoop.security.ssl.SSLFactory;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,7 +56,7 @@
 import org.apache.hadoop.classification.VisibleForTesting;
 
 @VisibleForTesting
-public class Fetcher<K, V> extends Thread {
+public class Fetcher<K, V> extends SubjectInheritingThread {
   
   private static final Logger LOG = LoggerFactory.getLogger(Fetcher.class);
   
@@ -187,7 +188,7 @@ public Fetcher(JobConf job, TaskAttemptID reduceId,
     }
   }
   
-  public void run() {
+  public void work() {
     try {
       while (!stopped && !Thread.currentThread().isInterrupted()) {
         MapHost host = null;
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/LocalFetcher.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/LocalFetcher.java
index dc563eeab4d..9ad5db1a0ba 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/LocalFetcher.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/LocalFetcher.java
@@ -71,7 +71,7 @@ public LocalFetcher(JobConf job, TaskAttemptID reduceId,
     setDaemon(true);
   }
 
-  public void run() {
+  public void work() {
     // Create a worklist of task attempts to work over.
     Set<TaskAttemptID> maps = new HashSet<TaskAttemptID>();
     for (TaskAttemptID map : localMapFiles.keySet()) {
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/MergeThread.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/MergeThread.java
index c617569da33..9bf3edc1a74 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/MergeThread.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/MergeThread.java
@@ -26,10 +26,11 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.hadoop.classification.VisibleForTesting;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-abstract class MergeThread<T,K,V> extends Thread {
+abstract class MergeThread<T,K,V> extends SubjectInheritingThread {
   
   private static final Logger LOG = LoggerFactory.getLogger(MergeThread.class);
 
@@ -78,7 +79,7 @@ public synchronized void waitForMerge() throws InterruptedException {
     }
   }
 
-  public void run() {
+  public void work() {
     while (true) {
       List<T> inputs = null;
       try {
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/ShuffleSchedulerImpl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/ShuffleSchedulerImpl.java
index 173cd093e9f..5e0dc0b3ba8 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/ShuffleSchedulerImpl.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/task/reduce/ShuffleSchedulerImpl.java
@@ -49,6 +49,7 @@
 import org.apache.hadoop.mapreduce.task.reduce.MapHost.State;
 import org.apache.hadoop.util.Progress;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -554,13 +555,13 @@ public int compareTo(Delayed o) {
   /**
    * A thread that takes hosts off of the penalty list when the timer expires.
    */
-  private class Referee extends Thread {
+  private class Referee extends SubjectInheritingThread {
     public Referee() {
       setName("ShufflePenaltyReferee");
       setDaemon(true);
     }
 
-    public void run() {
+    public void work() {
       try {
         while (true) {
           // take the first host that has an expired penalty
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/util/ProcessTree.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/util/ProcessTree.java
index 585a21d5682..473dff3b943 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/util/ProcessTree.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/util/ProcessTree.java
@@ -24,6 +24,7 @@
 import org.apache.hadoop.classification.InterfaceStability;
 import org.apache.hadoop.util.Shell.ExitCodeException;
 import org.apache.hadoop.util.Shell.ShellCommandExecutor;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -326,7 +327,7 @@ public static boolean isProcessGroupAlive(String pgrpId) {
   /**
    * Helper thread class that kills process-tree with SIGKILL in background
    */
-  static class SigKillThread extends Thread {
+  static class SigKillThread extends SubjectInheritingThread {
     private String pid = null;
     private boolean isProcessGroup = false;
 
@@ -339,7 +340,7 @@ private SigKillThread(String pid, boolean isProcessGroup, long interval) {
       sleepTimeBeforeSigKill = interval;
     }
 
-    public void run() {
+    public void work() {
       sigKillInCurrentThread(pid, isProcessGroup, sleepTimeBeforeSigKill);
     }
   }
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestIndexCache.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestIndexCache.java
index e624b0304f1..0b79d56de9d 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestIndexCache.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestIndexCache.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.FSDataOutputStream;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.mapreduce.MRJobConfig;
 
 import org.junit.jupiter.api.BeforeEach;
@@ -221,9 +222,9 @@ public void testRemoveMap() throws Exception {
     
     // run multiple times
     for (int i = 0; i < 20; ++i) {
-      Thread getInfoThread = new Thread() {
+      Thread getInfoThread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           try {
             cache.getIndexInformation("bigIndex", partsPerMap, big, user);
           } catch (Exception e) {
@@ -231,9 +232,9 @@ public void run() {
           }
         }
       };
-      Thread removeMapThread = new Thread() {
+      Thread removeMapThread = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           cache.removeMap("bigIndex");
         }
       };
@@ -266,9 +267,9 @@ public void testCreateRace() throws Exception {
     // run multiple instances
     Thread[] getInfoThreads = new Thread[50];
     for (int i = 0; i < 50; i++) {
-      getInfoThreads[i] = new Thread() {
+      getInfoThreads[i] = new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           try {
             cache.getIndexInformation("racyIndex", partsPerMap, racy, user);
             cache.removeMap("racyIndex");
@@ -285,9 +286,9 @@ public void run() {
 
     final Thread mainTestThread = Thread.currentThread();
 
-    Thread timeoutThread = new Thread() {
+    Thread timeoutThread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           Thread.sleep(15000);
           mainTestThread.interrupt();
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestLocatedFileStatusFetcher.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestLocatedFileStatusFetcher.java
index 0d24cbc323a..0de980215c6 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestLocatedFileStatusFetcher.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestLocatedFileStatusFetcher.java
@@ -35,6 +35,7 @@
 import org.apache.hadoop.fs.PathFilter;
 import org.apache.hadoop.test.AbstractHadoopTestBase;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -77,9 +78,9 @@ public boolean accept(Path path) {
           }
         }, true);
 
-    Thread t = new Thread() {
+    SubjectInheritingThread t = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           fetcher.getFileStatuses();
         } catch (Exception e) {
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestTaskProgressReporter.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestTaskProgressReporter.java
index ef43beaa5f7..f9917562419 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestTaskProgressReporter.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/test/java/org/apache/hadoop/mapred/TestTaskProgressReporter.java
@@ -34,6 +34,7 @@
 import org.apache.hadoop.mapreduce.MRJobConfig;
 import org.apache.hadoop.mapreduce.checkpoint.TaskCheckpointID;
 import org.apache.hadoop.util.ExitUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -252,7 +253,7 @@ public void uncaughtException(Thread th, Throwable ex) {
     task.setConf(conf);
     DummyTaskReporter reporter = new DummyTaskReporter(task);
     reporter.startDiskLimitCheckerThreadIfNeeded();
-    Thread t = new Thread(reporter);
+    Thread t = new SubjectInheritingThread(reporter);
     t.setUncaughtExceptionHandler(h);
     reporter.setProgressFlag();
     t.start();
@@ -273,7 +274,7 @@ public void testTaskProgress() throws Exception {
     Task task = new DummyTask();
     task.setConf(job);
     DummyTaskReporter reporter = new DummyTaskReporter(task);
-    Thread t = new Thread(reporter);
+    Thread t = new SubjectInheritingThread(reporter);
     t.start();
     Thread.sleep(2100);
     task.setTaskDone();
@@ -328,7 +329,7 @@ public void uncaughtException(Thread th, Throwable ex) {
     Task task = new DummyTask();
     task.setConf(conf);
     DummyTaskReporter reporter = new DummyTaskReporter(task);
-    Thread t = new Thread(reporter);
+    Thread t = new SubjectInheritingThread(reporter);
     t.setUncaughtExceptionHandler(h);
     reporter.setProgressFlag();
 
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestHistoryFileManager.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestHistoryFileManager.java
index db7d6a980ed..ae6f488a6a6 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestHistoryFileManager.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestHistoryFileManager.java
@@ -41,6 +41,7 @@
 import org.apache.hadoop.mapreduce.v2.hs.HistoryFileManager.HistoryFileInfo;
 import org.apache.hadoop.mapreduce.v2.jobhistory.JHAdminConfig;
 import org.apache.hadoop.mapreduce.v2.jobhistory.JobIndexInfo;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
 import org.apache.hadoop.yarn.util.Clock;
@@ -184,9 +185,9 @@ public void testCreateDirsWithFileSystemBecomingAvailBeforeTimeout()
     dfsCluster.getFileSystem().setSafeMode(
         SafeModeAction.ENTER);
     assertTrue(dfsCluster.getFileSystem().isInSafeMode());
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           Thread.sleep(500);
           dfsCluster.getFileSystem().setSafeMode(
@@ -209,9 +210,9 @@ public void testCreateDirsWithFileSystemNotBecomingAvailBeforeTimeout()
     assertTrue(dfsCluster.getFileSystem().isInSafeMode());
     final ControlledClock clock = new ControlledClock();
     clock.setTime(1);
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           Thread.sleep(500);
           clock.setTime(3000);
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryEvents.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryEvents.java
index b3b66e560d9..08559399dbd 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryEvents.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestJobHistoryEvents.java
@@ -38,6 +38,7 @@
 import org.apache.hadoop.mapreduce.v2.app.job.Task;
 import org.apache.hadoop.mapreduce.v2.app.job.TaskAttempt;
 import org.apache.hadoop.service.Service;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ContainerId;
 import org.apache.hadoop.yarn.event.EventHandler;
 import org.junit.jupiter.api.Test;
@@ -254,7 +255,7 @@ protected EventHandler<JobHistoryEvent> createJobHistoryHandler(
         @Override
         protected void serviceStart() {
           // Don't start any event draining thread.
-          super.eventHandlingThread = new Thread();
+          super.eventHandlingThread = new SubjectInheritingThread();
           super.eventHandlingThread.start();
         }
       };
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestUnnecessaryBlockingOnHistoryFileInfo.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestUnnecessaryBlockingOnHistoryFileInfo.java
index 2a7a9f3c80b..b18e0c041da 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestUnnecessaryBlockingOnHistoryFileInfo.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-hs/src/test/java/org/apache/hadoop/mapreduce/v2/hs/TestUnnecessaryBlockingOnHistoryFileInfo.java
@@ -26,6 +26,7 @@
 import org.apache.hadoop.mapreduce.v2.app.job.Job;
 import org.apache.hadoop.mapreduce.v2.jobhistory.JHAdminConfig;
 import org.apache.hadoop.mapreduce.v2.jobhistory.JobIndexInfo;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.impl.pb.ApplicationIdPBImpl;
 
 import org.junit.jupiter.api.AfterAll;
@@ -106,7 +107,7 @@ public void testTwoThreadsQueryingDifferentJobOfSameUser()
        * files in one child thread.
        */
       createJhistFile(job1);
-      webRequest1 = new Thread(
+      webRequest1 = new SubjectInheritingThread(
           new Runnable() {
             @Override
             public void run() {
@@ -136,7 +137,7 @@ public void run() {
        * will also see the job history files for job1.
        */
       createJhistFile(job2);
-      webRequest2 = new Thread(
+      webRequest2 = new SubjectInheritingThread(
           new Runnable() {
             @Override
             public void run() {
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/FailingMapper.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/FailingMapper.java
index 33a60681a35..3c36f13dcc3 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/FailingMapper.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/FailingMapper.java
@@ -22,6 +22,7 @@
 
 import org.apache.hadoop.io.Text;
 import org.apache.hadoop.mapreduce.Mapper;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 /**
  * Fails the Mapper. First attempt throws exception. Rest do System.exit.
@@ -33,9 +34,9 @@ public void map(Text key, Text value,
 
     // Just create a non-daemon thread which hangs forever. MR AM should not be
     // hung by this.
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         synchronized (this) {
           try {
             wait();
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/JHLogAnalyzer.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/JHLogAnalyzer.java
index 125dad5cbe1..c9c1cc48256 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/JHLogAnalyzer.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/JHLogAnalyzer.java
@@ -44,6 +44,7 @@
 import org.apache.hadoop.mapred.*;
 import org.apache.hadoop.util.ReflectionUtils;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -178,7 +179,7 @@ enum StatSeries {
     public String toString() {return statName;}
   }
 
-  private static class FileCreateDaemon extends Thread {
+  private static class FileCreateDaemon extends SubjectInheritingThread {
     private static final int NUM_CREATE_THREADS = 10;
     private static volatile int numFinishedThreads;
     private static volatile int numRunningThreads;
@@ -194,7 +195,7 @@ private static class FileCreateDaemon extends Thread {
       this.end = end;
     }
 
-    public void run() {
+    public void work() {
       try {
         for(int i=start; i < end; i++) {
           String name = getFileName(i);
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/loadGenerator/LoadGeneratorMR.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/loadGenerator/LoadGeneratorMR.java
index 044c77c0853..de6668ffdf3 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/loadGenerator/LoadGeneratorMR.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/fs/loadGenerator/LoadGeneratorMR.java
@@ -48,6 +48,7 @@
 import org.apache.hadoop.mapred.Reporter;
 import org.apache.hadoop.mapred.TextOutputFormat;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -308,7 +309,7 @@ public void configure(JobConf job) {
       getArgsFromConfiguration(jobConf);
     }
 
-    private class ProgressThread extends Thread {
+    private class ProgressThread extends SubjectInheritingThread {
 
       boolean keepGoing; // while this is true, thread runs.
       private Reporter reporter;
@@ -318,7 +319,7 @@ public ProgressThread(final Reporter r) {
         this.keepGoing = true;
       }
 
-      public void run() {
+      public void work() {
         while (keepGoing) {
           if (!ProgressThread.interrupted()) {
             try {
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/ReliabilityTest.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/ReliabilityTest.java
index 303857bf70e..0952f089eda 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/ReliabilityTest.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/ReliabilityTest.java
@@ -39,6 +39,7 @@
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Tool;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -200,8 +201,8 @@ private void checkJobExitStatus(int status, String jobName) {
   private void runTest(final JobClient jc, final Configuration conf,
       final String jobClass, final String[] args, KillTaskThread killTaskThread,
       KillTrackerThread killTrackerThread) throws Exception {
-    Thread t = new Thread("Job Test") {
-      public void run() {
+    SubjectInheritingThread t = new SubjectInheritingThread("Job Test") {
+      public void work() {
         try {
           Class<?> jobClassObj = conf.getClassByName(jobClass);
           int status = ToolRunner.run(conf, (Tool)(jobClassObj.newInstance()),
@@ -249,7 +250,7 @@ public void run() {
     t.join();
   }
 
-  private class KillTrackerThread extends Thread {
+  private class KillTrackerThread extends SubjectInheritingThread {
     private volatile boolean killed = false;
     private JobClient jc;
     private RunningJob rJob;
@@ -281,7 +282,7 @@ public void setRunningJob(RunningJob rJob) {
     public void kill() {
       killed = true;
     }
-    public void run() {
+    public void work() {
       stopStartTrackers(true);
       if (!onlyMapsProgress) {
         stopStartTrackers(false);
@@ -392,7 +393,7 @@ private String convertTrackerNameToHostName(String trackerName) {
 
   }
 
-  private class KillTaskThread extends Thread {
+  private class KillTaskThread extends SubjectInheritingThread {
 
     private volatile boolean killed = false;
     private RunningJob rJob;
@@ -416,7 +417,7 @@ public void setRunningJob(RunningJob rJob) {
     public void kill() {
       killed = true;
     }
-    public void run() {
+    public void work() {
       killBasedOnProgress(true);
       if (!onlyMapsProgress) {
         killBasedOnProgress(false);
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/TestCollect.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/TestCollect.java
index 83ea506bcd0..35b3d145526 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/TestCollect.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/TestCollect.java
@@ -21,6 +21,7 @@
 import org.apache.hadoop.io.*;
 import org.apache.hadoop.mapred.UtilsForTests.RandomInputFormat;
 import org.apache.hadoop.mapreduce.MRConfig;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 
 import java.io.*;
@@ -51,14 +52,14 @@ public void map(Text key, Text val,
                     final OutputCollector<IntWritable, IntWritable> out,
                     Reporter reporter) throws IOException {
       // Class for calling collect in separate threads
-      class CollectFeeder extends Thread {
+      class CollectFeeder extends SubjectInheritingThread {
         int id; // id for the thread
         
         public CollectFeeder(int id) {
           this.id = id;
         }
         
-        public void run() {
+        public void work() {
           for (int j = 1; j <= NUM_COLLECTS_PER_THREAD; j++) {
             try {
               out.collect(new IntWritable((id * NUM_COLLECTS_PER_THREAD) + j), 
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/jobcontrol/TestJobControl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/jobcontrol/TestJobControl.java
index 7f26bb33e81..0d55f3f986e 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/jobcontrol/TestJobControl.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/jobcontrol/TestJobControl.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.mapred.JobConf;
 import org.apache.hadoop.mapred.JobID;
 import org.apache.hadoop.mapreduce.lib.jobcontrol.ControlledJob;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
@@ -110,7 +111,7 @@ public static void doJobControlTest() throws Exception {
     theControl.addJob(job_3);
     theControl.addJob(job_4);
 
-    Thread theController = new Thread(theControl);
+    Thread theController = new SubjectInheritingThread(theControl);
     theController.start();
     while (!theControl.allFinished()) {
 
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/jobcontrol/TestLocalJobControl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/jobcontrol/TestLocalJobControl.java
index 92a5868a56b..f64a5270891 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/jobcontrol/TestLocalJobControl.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/jobcontrol/TestLocalJobControl.java
@@ -25,6 +25,7 @@
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.mapred.HadoopTestCase;
 import org.apache.hadoop.mapred.JobConf;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -115,7 +116,7 @@ public void testLocalJobControlDataCopy() throws Exception {
     theControl.addJob(job_3);
     theControl.addJob(job_4);
 
-    Thread theController = new Thread(theControl);
+    Thread theController = new SubjectInheritingThread(theControl);
     theController.start();
     while (!theControl.allFinished()) {
       LOG.debug("Jobs in waiting state: " + theControl.getWaitingJobs().size());
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/pipes/TestPipeApplication.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/pipes/TestPipeApplication.java
index 3c6cbbe9ea5..6e7899a95a3 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/pipes/TestPipeApplication.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapred/pipes/TestPipeApplication.java
@@ -519,8 +519,8 @@ private static class SocketCleaner extends PingSocketCleaner {
     }
 
     @Override
-    public void run() {
-      super.run();
+    public void work() {
+      super.work();
     }
 
     protected void closeSocketInternal(Socket clientSocket) {
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/TestLocalRunner.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/TestLocalRunner.java
index d97ea5c8f7a..1bcdd26a1cb 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/TestLocalRunner.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/TestLocalRunner.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.mapreduce.lib.input.FileSplit;
 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
 import org.apache.hadoop.util.ReflectionUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.slf4j.Logger;
@@ -317,8 +318,8 @@ public void testMultiMaps() throws Exception {
     FileOutputFormat.setOutputPath(job, outputPath);
 
     final Thread toInterrupt = Thread.currentThread();
-    Thread interrupter = new Thread() {
-      public void run() {
+    SubjectInheritingThread interrupter = new SubjectInheritingThread() {
+      public void work() {
         try {
           Thread.sleep(120*1000); // 2m
           toInterrupt.interrupt();
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/lib/jobcontrol/TestMapReduceJobControl.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/lib/jobcontrol/TestMapReduceJobControl.java
index 1bae2b0fe2c..431eac646fe 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/lib/jobcontrol/TestMapReduceJobControl.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/lib/jobcontrol/TestMapReduceJobControl.java
@@ -28,6 +28,7 @@
 import org.apache.hadoop.mapred.HadoopTestCase;
 import org.apache.hadoop.mapreduce.Job;
 import org.apache.hadoop.mapreduce.MapReduceTestUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 import org.slf4j.Logger;
@@ -112,7 +113,7 @@ private JobControl createDependencies(Configuration conf, Job job1)
     theControl.addJob(cjob2);
     theControl.addJob(cjob3);
     theControl.addJob(cjob4);
-    Thread theController = new Thread(theControl);
+    Thread theController = new SubjectInheritingThread(theControl);
     theController.start();
     return theControl;
   }
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/lib/jobcontrol/TestMapReduceJobControlWithMocks.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/lib/jobcontrol/TestMapReduceJobControlWithMocks.java
index 0b423797e5c..efa402a9eff 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/lib/jobcontrol/TestMapReduceJobControlWithMocks.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/lib/jobcontrol/TestMapReduceJobControlWithMocks.java
@@ -30,6 +30,7 @@
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.mapreduce.Job;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -148,7 +149,7 @@ private ControlledJob createFailedControlledJob(JobControl jobControl,
   }
 
   private void runJobControl(JobControl jobControl) {
-    Thread controller = new Thread(jobControl);
+    Thread controller = new SubjectInheritingThread(jobControl);
     controller.start();
     waitTillAllFinished(jobControl);
   }
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/MiniMRYarnCluster.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/MiniMRYarnCluster.java
index b3533482b52..77f3322977e 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/MiniMRYarnCluster.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/MiniMRYarnCluster.java
@@ -45,6 +45,7 @@
 import org.apache.hadoop.service.Service;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.JarFinder;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
 import org.apache.hadoop.yarn.server.MiniYARNCluster;
@@ -266,8 +267,8 @@ public synchronized void serviceStart() throws Exception {
         }
         historyServer = new JobHistoryServer();
         historyServer.init(getConfig());
-        new Thread() {
-          public void run() {
+        new SubjectInheritingThread() {
+          public void work() {
             historyServer.start();
           };
         }.start();
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/main/java/org/apache/hadoop/mapred/nativetask/StatusReportChecker.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/main/java/org/apache/hadoop/mapred/nativetask/StatusReportChecker.java
index 6a437b123c8..5e0d1667c11 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/main/java/org/apache/hadoop/mapred/nativetask/StatusReportChecker.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/main/java/org/apache/hadoop/mapred/nativetask/StatusReportChecker.java
@@ -22,6 +22,7 @@
 import org.apache.hadoop.mapred.Task.TaskReporter;
 import org.apache.hadoop.mapreduce.TaskCounter;
 import org.apache.hadoop.mapreduce.lib.input.FileInputFormatCounter;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -85,7 +86,7 @@ public synchronized void start() {
       // init counters used by native side,
       // so they will have correct display name
       initUsedCounters();
-      checker = new Thread(this);
+      checker = new SubjectInheritingThread(this);
       checker.setDaemon(true);
       checker.start();
     }
diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-examples/src/main/java/org/apache/hadoop/examples/terasort/TeraInputFormat.java b/hadoop-mapreduce-project/hadoop-mapreduce-examples/src/main/java/org/apache/hadoop/examples/terasort/TeraInputFormat.java
index 3ce6936c3d7..5b558b391fa 100644
--- a/hadoop-mapreduce-project/hadoop-mapreduce-examples/src/main/java/org/apache/hadoop/examples/terasort/TeraInputFormat.java
+++ b/hadoop-mapreduce-project/hadoop-mapreduce-examples/src/main/java/org/apache/hadoop/examples/terasort/TeraInputFormat.java
@@ -42,6 +42,7 @@
 import org.apache.hadoop.util.IndexedSortable;
 import org.apache.hadoop.util.QuickSort;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.util.functional.FutureIO;
 
 import static org.apache.hadoop.fs.Options.OpenFileOptions.FS_OPTION_OPENFILE_READ_POLICY;
@@ -145,11 +146,11 @@ public static void writePartitionFile(final JobContext job,
     for(int i=0; i < samples; ++i) {
       final int idx = i;
       samplerReader[i] = 
-        new Thread (threadGroup,"Sampler Reader " + idx) {
+        new SubjectInheritingThread (threadGroup, "Sampler Reader " + idx) {
         {
           setDaemon(true);
         }
-        public void run() {
+        public void work() {
           long records = 0;
           try {
             TaskAttemptContext context = new TaskAttemptContextImpl(
diff --git a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/ITestS3AIOStatisticsContext.java b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/ITestS3AIOStatisticsContext.java
index 2bc342717a1..970673a8b15 100644
--- a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/ITestS3AIOStatisticsContext.java
+++ b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/ITestS3AIOStatisticsContext.java
@@ -38,6 +38,7 @@
 import org.apache.hadoop.fs.statistics.IOStatisticsContext;
 import org.apache.hadoop.fs.statistics.impl.IOStatisticsContextImpl;
 import org.apache.hadoop.util.concurrent.HadoopExecutors;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.util.functional.CloseableTaskPoolSubmitter;
 import org.apache.hadoop.util.functional.TaskPool;
 
@@ -453,7 +454,7 @@ public void testListingThroughTaskPool() throws Throwable {
    * If constructed with an IOStatisticsContext then
    * that context is switched to before performing the IO.
    */
-  private class TestWorkerThread extends Thread implements Runnable {
+  private class TestWorkerThread extends SubjectInheritingThread implements Runnable {
     private final Path workerThreadPath;
 
     private final IOStatisticsContext ioStatisticsContext;
@@ -471,7 +472,7 @@ private class TestWorkerThread extends Thread implements Runnable {
     }
 
     @Override
-    public void run() {
+    public void work() {
       // Setting the worker thread's name.
       Thread.currentThread().setName("worker thread");
       S3AFileSystem fs = getFileSystem();
diff --git a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/scale/ITestS3AConcurrentOps.java b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/scale/ITestS3AConcurrentOps.java
index 96c59c266a6..6e4d62129c4 100644
--- a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/scale/ITestS3AConcurrentOps.java
+++ b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/scale/ITestS3AConcurrentOps.java
@@ -37,6 +37,7 @@
 import org.apache.hadoop.fs.contract.ContractTestUtils.NanoTimer;
 import org.apache.hadoop.fs.s3a.S3AFileSystem;
 import org.apache.hadoop.test.tags.ScaleTest;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.assertj.core.api.Assertions;
 import org.junit.jupiter.api.AfterEach;
@@ -135,7 +136,7 @@ concurrentRenames, new ThreadFactory() {
           private AtomicInteger count = new AtomicInteger(0);
 
           public Thread newThread(Runnable r) {
-            return new Thread(r,
+            return new SubjectInheritingThread(r,
                 "testParallelRename" + count.getAndIncrement());
           }
         });
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/AzureFileSystemThreadPoolExecutor.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/AzureFileSystemThreadPoolExecutor.java
index 7cd1821c3da..98d28d1ea42 100644
--- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/AzureFileSystemThreadPoolExecutor.java
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/AzureFileSystemThreadPoolExecutor.java
@@ -27,6 +27,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -256,7 +257,7 @@ public AzureFileSystemThreadFactory(String prefix) {
 
     @Override
     public Thread newThread(Runnable r) {
-      Thread t = new Thread(r);
+      Thread t = new SubjectInheritingThread(r);
 
       // Use current thread name as part in naming thread such that use of
       // same file system object will have unique names.
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/BlockBlobAppendStream.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/BlockBlobAppendStream.java
index 4c8d5fb6a5f..c19bbe48fc3 100644
--- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/BlockBlobAppendStream.java
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/BlockBlobAppendStream.java
@@ -44,6 +44,7 @@
 import org.apache.hadoop.fs.impl.StoreImplementationUtils;
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.util.Preconditions;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.commons.lang3.StringUtils;
 
 import org.apache.hadoop.fs.FSExceptionMessages;
@@ -821,7 +822,7 @@ class UploaderThreadFactory implements ThreadFactory {
 
     @Override
     public Thread newThread(Runnable r) {
-      Thread t = new Thread(r);
+      Thread t = new SubjectInheritingThread(r);
       t.setName(String.format("%s-%d", THREAD_ID_PREFIX,
           threadSequenceNumber.getAndIncrement()));
       return t;
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/SelfRenewingLease.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/SelfRenewingLease.java
index 989c3ba6d93..8ab568fdc3b 100644
--- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/SelfRenewingLease.java
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/SelfRenewingLease.java
@@ -21,6 +21,7 @@
 import org.apache.hadoop.fs.azure.StorageInterface.CloudBlobWrapper;
 
 import org.apache.hadoop.classification.VisibleForTesting;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import com.microsoft.azure.storage.AccessCondition;
 import com.microsoft.azure.storage.StorageException;
@@ -105,7 +106,7 @@ public SelfRenewingLease(CloudBlobWrapper blobWrapper, boolean throwIfPresent)
         }
       }
     }
-    renewer = new Thread(new Renewer());
+    renewer = new SubjectInheritingThread(new Renewer());
 
     // A Renewer running should not keep JVM from exiting, so make it a daemon.
     renewer.setDaemon(true);
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/metrics/BandwidthGaugeUpdater.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/metrics/BandwidthGaugeUpdater.java
index d3fe4aefeb0..d0a1bd0e7fb 100644
--- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/metrics/BandwidthGaugeUpdater.java
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azure/metrics/BandwidthGaugeUpdater.java
@@ -22,6 +22,7 @@
 import java.util.Date;
 
 import org.apache.hadoop.classification.InterfaceAudience;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 /**
  * Internal implementation class to help calculate the current bytes
@@ -67,7 +68,7 @@ public BandwidthGaugeUpdater(AzureFileSystemInstrumentation instrumentation,
     this.windowSizeMs = windowSizeMs;
     this.instrumentation = instrumentation;
     if (!manualUpdateTrigger) {
-      uploadBandwidthUpdater = new Thread(new UploadBandwidthUpdater(), THREAD_NAME);
+      uploadBandwidthUpdater = new SubjectInheritingThread(new UploadBandwidthUpdater(), THREAD_NAME);
       uploadBandwidthUpdater.setDaemon(true);
       uploadBandwidthUpdater.start();
     }
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ListActionTaker.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ListActionTaker.java
index 74f5aa4ffb5..7a0e42c46f2 100644
--- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ListActionTaker.java
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ListActionTaker.java
@@ -41,6 +41,7 @@
 import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultEntrySchema;
 import org.apache.hadoop.fs.azurebfs.contracts.services.ListResultSchema;
 import org.apache.hadoop.fs.azurebfs.utils.TracingContext;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import static org.apache.hadoop.fs.azurebfs.constants.AbfsHttpConstants.ROOT_PATH;
 import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.DEFAULT_AZURE_LIST_MAX_RESULTS;
@@ -151,7 +152,7 @@ public boolean listRecursiveAndTakeAction()
     Thread producerThread = null;
     try {
       ListBlobQueue listBlobQueue = createListBlobQueue(configuration);
-      producerThread = new Thread(() -> {
+      producerThread = new SubjectInheritingThread(() -> {
         try {
           produceConsumableList(listBlobQueue);
         } catch (AzureBlobFileSystemException e) {
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ReadBufferManagerV1.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ReadBufferManagerV1.java
index e476f6d7446..240a6186666 100644
--- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ReadBufferManagerV1.java
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ReadBufferManagerV1.java
@@ -27,6 +27,7 @@
 import java.util.concurrent.CountDownLatch;
 
 import org.apache.hadoop.fs.azurebfs.utils.TracingContext;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.classification.VisibleForTesting;
 
 /**
@@ -92,7 +93,7 @@ void init() {
       getFreeList().add(i);
     }
     for (int i = 0; i < NUM_THREADS; i++) {
-      Thread t = new Thread(new ReadBufferWorker(i, this));
+      Thread t = new SubjectInheritingThread(new ReadBufferWorker(i, this));
       t.setDaemon(true);
       threads[i] = t;
       t.setName("ABFS-prefetch-" + i);
diff --git a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ReadBufferManagerV2.java b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ReadBufferManagerV2.java
index c7e6e4c3d28..7943755b979 100644
--- a/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ReadBufferManagerV2.java
+++ b/hadoop-tools/hadoop-azure/src/main/java/org/apache/hadoop/fs/azurebfs/services/ReadBufferManagerV2.java
@@ -45,6 +45,7 @@
 
 import org.apache.hadoop.fs.azurebfs.utils.TracingContext;
 import org.apache.hadoop.classification.VisibleForTesting;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.HUNDRED_D;
 
@@ -978,7 +979,7 @@ private static void setIsConfigured(boolean configured) {
 
     @Override
     public Thread newThread(Runnable r) {
-      Thread t = new Thread(r, "ReadAheadV2-WorkerThread-" + count++);
+      Thread t = new SubjectInheritingThread(r, "ReadAheadV2-WorkerThread-" + count++);
       t.setDaemon(true);
       return t;
     }
diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestAzureConcurrentOutOfBandIo.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestAzureConcurrentOutOfBandIo.java
index f28a15fd714..c008d64386b 100644
--- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestAzureConcurrentOutOfBandIo.java
+++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestAzureConcurrentOutOfBandIo.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.fs.azure.integration.AzureTestUtils;
 import org.apache.hadoop.fs.permission.FsPermission;
 import org.apache.hadoop.fs.permission.PermissionStatus;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 /**
  * Handle OOB IO into a shared container.
@@ -74,7 +75,7 @@ public DataBlockWriter(AzureBlobStorageTestAccount testAccount, String key) {
      * Start writing blocks to Azure storage.
      */
     public void startWriting() {
-      runner = new Thread(this); // Create the block writer thread.
+      runner = new SubjectInheritingThread(this); // Create the block writer thread.
       runner.start(); // Start the block writer thread.
     }
 
diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestFileSystemOperationsExceptionHandlingMultiThreaded.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestFileSystemOperationsExceptionHandlingMultiThreaded.java
index ab175ba6c5c..19080d031b6 100644
--- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestFileSystemOperationsExceptionHandlingMultiThreaded.java
+++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestFileSystemOperationsExceptionHandlingMultiThreaded.java
@@ -31,6 +31,7 @@
 import org.apache.hadoop.fs.permission.FsAction;
 import org.apache.hadoop.fs.permission.FsPermission;
 import org.apache.hadoop.io.IOUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import static org.apache.hadoop.fs.azure.ExceptionHandlingTestHelper.*;
 
@@ -94,7 +95,7 @@ public void testMultiThreadedBlockBlobReadScenario() throws Throwable {
       Path testFilePath1 = new Path(base, "test1.dat");
       Path renamePath = new Path(base, "test2.dat");
       getInputStreamToTest(fs, testFilePath1);
-      Thread renameThread = new Thread(
+      Thread renameThread = new SubjectInheritingThread(
           new RenameThread(fs, testFilePath1, renamePath));
       renameThread.start();
 
@@ -121,7 +122,7 @@ public void testMultiThreadBlockBlobSeekScenario() throws Throwable {
       Path renamePath = new Path(base, "test2.dat");
 
       getInputStreamToTest(fs, testFilePath1);
-      Thread renameThread = new Thread(
+      Thread renameThread = new SubjectInheritingThread(
               new RenameThread(fs, testFilePath1, renamePath));
       renameThread.start();
 
@@ -142,7 +143,7 @@ public void testMultiThreadedPageBlobSetPermissionScenario()
       createEmptyFile(
           getPageBlobTestStorageAccount(),
           testPath);
-      Thread t = new Thread(new DeleteThread(fs, testPath));
+      Thread t = new SubjectInheritingThread(new DeleteThread(fs, testPath));
       t.start();
       while (t.isAlive()) {
         fs.setPermission(testPath,
@@ -161,7 +162,7 @@ public void testMultiThreadedBlockBlobSetPermissionScenario()
       throws Throwable {
     assertThrows(FileNotFoundException.class, () -> {
       createEmptyFile(createTestAccount(), testPath);
-      Thread t = new Thread(new DeleteThread(fs, testPath));
+      Thread t = new SubjectInheritingThread(new DeleteThread(fs, testPath));
       t.start();
       while (t.isAlive()) {
         fs.setPermission(testPath,
@@ -179,7 +180,7 @@ public void testMultiThreadedBlockBlobSetPermissionScenario()
   public void testMultiThreadedPageBlobOpenScenario() throws Throwable {
     assertThrows(FileNotFoundException.class, () -> {
       createEmptyFile(createTestAccount(), testPath);
-      Thread t = new Thread(new DeleteThread(fs, testPath));
+      Thread t = new SubjectInheritingThread(new DeleteThread(fs, testPath));
       t.start();
       while (t.isAlive()) {
         inputStream = fs.open(testPath);
@@ -200,7 +201,7 @@ public void testMultiThreadedBlockBlobOpenScenario() throws Throwable {
       createEmptyFile(
           getPageBlobTestStorageAccount(),
           testPath);
-      Thread t = new Thread(new DeleteThread(fs, testPath));
+      Thread t = new SubjectInheritingThread(new DeleteThread(fs, testPath));
       t.start();
 
       while (t.isAlive()) {
@@ -219,7 +220,7 @@ public void testMultiThreadedBlockBlobOpenScenario() throws Throwable {
   public void testMultiThreadedBlockBlobSetOwnerScenario() throws Throwable {
     assertThrows(FileNotFoundException.class, () -> {
       createEmptyFile(createTestAccount(), testPath);
-      Thread t = new Thread(new DeleteThread(fs, testPath));
+      Thread t = new SubjectInheritingThread(new DeleteThread(fs, testPath));
       t.start();
       while (t.isAlive()) {
         fs.setOwner(testPath, "testowner", "testgroup");
@@ -237,7 +238,7 @@ public void testMultiThreadedPageBlobSetOwnerScenario() throws Throwable {
       createEmptyFile(
           getPageBlobTestStorageAccount(),
           testPath);
-      Thread t = new Thread(new DeleteThread(fs, testPath));
+      Thread t = new SubjectInheritingThread(new DeleteThread(fs, testPath));
       t.start();
       while (t.isAlive()) {
         fs.setOwner(testPath, "testowner", "testgroup");
@@ -253,7 +254,7 @@ public void testMultiThreadedPageBlobSetOwnerScenario() throws Throwable {
   public void testMultiThreadedBlockBlobListStatusScenario() throws Throwable {
     assertThrows(FileNotFoundException.class, () -> {
       createTestFolder(createTestAccount(), testFolderPath);
-      Thread t = new Thread(new DeleteThread(fs, testFolderPath));
+      Thread t = new SubjectInheritingThread(new DeleteThread(fs, testFolderPath));
       t.start();
       while (t.isAlive()) {
         fs.listStatus(testFolderPath);
@@ -271,7 +272,7 @@ public void testMultiThreadedPageBlobListStatusScenario() throws Throwable {
       createTestFolder(
           getPageBlobTestStorageAccount(),
           testFolderPath);
-      Thread t = new Thread(new DeleteThread(fs, testFolderPath));
+      Thread t = new SubjectInheritingThread(new DeleteThread(fs, testFolderPath));
       t.start();
       while (t.isAlive()) {
         fs.listStatus(testFolderPath);
@@ -293,7 +294,7 @@ public void testMultiThreadedPageBlobReadScenario() throws Throwable {
       Path renamePath = new Path(base, "test2.dat");
 
       getInputStreamToTest(fs, testFilePath1);
-      Thread renameThread = new Thread(
+      Thread renameThread = new SubjectInheritingThread(
               new RenameThread(fs, testFilePath1, renamePath));
       renameThread.start();
 
@@ -318,7 +319,7 @@ public void testMultiThreadedPageBlobSeekScenario() throws Throwable {
       Path renamePath = new Path(base, "test2.dat");
 
       getInputStreamToTest(fs, testFilePath1);
-      Thread renameThread = new Thread(
+      Thread renameThread = new SubjectInheritingThread(
               new RenameThread(fs, testFilePath1, renamePath));
       renameThread.start();
 
diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestNativeAzureFileSystemLive.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestNativeAzureFileSystemLive.java
index 918866a73e5..fd2ba05a35a 100644
--- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestNativeAzureFileSystemLive.java
+++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/ITestNativeAzureFileSystemLive.java
@@ -29,6 +29,7 @@
 
 import org.apache.hadoop.fs.contract.ContractTestUtils;
 import org.apache.hadoop.io.IOUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.fs.FSDataOutputStream;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.permission.FsPermission;
@@ -67,7 +68,7 @@ public void testMultipleRenameFileOperationsToSameDestination()
     for (int i = 0; i < 10; i++) {
       final int threadNumber = i;
       Path src = path("test" + threadNumber);
-      threads.add(new Thread(() -> {
+      threads.add(new SubjectInheritingThread(() -> {
         try {
           latch.await(Long.MAX_VALUE, TimeUnit.SECONDS);
         } catch (InterruptedException e) {
@@ -155,9 +156,9 @@ public void testDeleteThrowsExceptionWithLeaseExistsErrorMessage()
     // Acquire the lease on the file in a background thread
     final CountDownLatch leaseAttemptComplete = new CountDownLatch(1);
     final CountDownLatch beginningDeleteAttempt = new CountDownLatch(1);
-    Thread t = new Thread() {
+    SubjectInheritingThread t = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         // Acquire the lease and then signal the main test thread.
         SelfRenewingLease lease = null;
         try {
diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/NativeAzureFileSystemBaseTest.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/NativeAzureFileSystemBaseTest.java
index ecf6e17b82a..7730d5283d7 100644
--- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/NativeAzureFileSystemBaseTest.java
+++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/NativeAzureFileSystemBaseTest.java
@@ -41,6 +41,7 @@
 import org.apache.hadoop.fs.contract.ContractTestUtils;
 import org.apache.hadoop.fs.permission.FsPermission;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.apache.hadoop.fs.azure.NativeAzureFileSystem.FolderRenamePending;
@@ -1643,9 +1644,9 @@ public void testLeaseAsDistributedLock() throws IllegalArgumentException,
     NativeAzureFileSystem nfs = (NativeAzureFileSystem) fs;
     String fullKey = nfs.pathToKey(nfs.makeAbsolute(new Path(LEASE_LOCK_FILE_KEY)));
 
-    Thread first = new Thread(new LeaseLockAction("first-thread", fullKey));
+    Thread first = new SubjectInheritingThread(new LeaseLockAction("first-thread", fullKey));
     first.start();
-    Thread second = new Thread(new LeaseLockAction("second-thread", fullKey));
+    Thread second = new SubjectInheritingThread(new LeaseLockAction("second-thread", fullKey));
     second.start();
     try {
 
diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/TestNativeAzureFileSystemConcurrency.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/TestNativeAzureFileSystemConcurrency.java
index f801f5e9dda..a5bd5538397 100644
--- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/TestNativeAzureFileSystemConcurrency.java
+++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/TestNativeAzureFileSystemConcurrency.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.fs.FileStatus;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -149,7 +150,7 @@ public void testMultiThreadedOperation() throws Exception {
       final ConcurrentLinkedQueue<Throwable> exceptionsEncountered = new ConcurrentLinkedQueue<Throwable>();
       for (int i = 0; i < numThreads; i++) {
         final Path threadLocalFile = new Path("/myFile" + i);
-        threads[i] = new Thread(new Runnable() {
+        threads[i] = new SubjectInheritingThread(new Runnable() {
           @Override
           public void run() {
             try {
diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/metrics/TestBandwidthGaugeUpdater.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/metrics/TestBandwidthGaugeUpdater.java
index 187aa02cceb..2ecc2592b47 100644
--- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/metrics/TestBandwidthGaugeUpdater.java
+++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azure/metrics/TestBandwidthGaugeUpdater.java
@@ -24,6 +24,7 @@
 import java.util.Date;
 
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 
 public class TestBandwidthGaugeUpdater {
@@ -56,7 +57,7 @@ public void testMultiThreaded() throws Exception {
         new BandwidthGaugeUpdater(instrumentation, 1000, true);
     Thread[] threads = new Thread[10];
     for (int i = 0; i < threads.length; i++) {
-      threads[i] = new Thread(new Runnable() {
+      threads[i] = new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           updater.blockDownloaded(new Date(), new Date(), 10);
diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemAppend.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemAppend.java
index 0f7e6d9009b..557333ff0fd 100644
--- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemAppend.java
+++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemAppend.java
@@ -63,6 +63,7 @@
 import org.apache.hadoop.fs.store.BlockUploadStatistics;
 import org.apache.hadoop.fs.store.DataBlocks;
 import org.apache.hadoop.test.LambdaTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
 import static java.net.HttpURLConnection.HTTP_OK;
@@ -1243,7 +1244,7 @@ public void testFlushSuccessWithConnectionResetOnResponseInvalidMd5() throws Exc
       out1.write(bytes1);
 
       //parallel flush call should lead to the first call failing because of md5 mismatch.
-      Thread parallelFlushThread = new Thread(() -> {
+      Thread parallelFlushThread = new SubjectInheritingThread(() -> {
         try {
           out1.hsync();
         } catch (IOException e) {
diff --git a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemRename.java b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemRename.java
index 73a826c601a..1251880cc2b 100644
--- a/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemRename.java
+++ b/hadoop-tools/hadoop-azure/src/test/java/org/apache/hadoop/fs/azurebfs/ITestAzureBlobFileSystemRename.java
@@ -70,6 +70,7 @@
 import org.apache.hadoop.fs.statistics.IOStatisticAssertions;
 import org.apache.hadoop.fs.statistics.IOStatistics;
 import org.apache.hadoop.test.LambdaTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.util.functional.FunctionRaisingIOE;
 
 import static java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT;
@@ -1014,7 +1015,7 @@ public void testParallelRenameForAtomicRenameShouldFail() throws Exception {
         .acquireLease(Mockito.anyString(), Mockito.anyInt(),
             Mockito.nullable(String.class),
             Mockito.any(TracingContext.class));
-    new Thread(() -> {
+    new SubjectInheritingThread(() -> {
       while (!leaseAcquired.get()) {}
       try {
         fs.rename(src, dst);
@@ -1064,7 +1065,7 @@ public void testAppendAtomicBlobDuringRename() throws Exception {
       return answer.callRealMethod();
     }).when(client).copyBlob(Mockito.any(Path.class), Mockito.any(Path.class),
         Mockito.nullable(String.class), Mockito.any(TracingContext.class));
-    new Thread(() -> {
+    new SubjectInheritingThread(() -> {
       while (!copyInProgress.get()) {}
       try {
         os.write(1);
diff --git a/hadoop-tools/hadoop-compat-bench/src/main/java/org/apache/hadoop/fs/compat/common/HdfsCompatShellScope.java b/hadoop-tools/hadoop-compat-bench/src/main/java/org/apache/hadoop/fs/compat/common/HdfsCompatShellScope.java
index 6b6596c38d8..e80ad8aca05 100644
--- a/hadoop-tools/hadoop-compat-bench/src/main/java/org/apache/hadoop/fs/compat/common/HdfsCompatShellScope.java
+++ b/hadoop-tools/hadoop-compat-bench/src/main/java/org/apache/hadoop/fs/compat/common/HdfsCompatShellScope.java
@@ -22,6 +22,7 @@
 import org.apache.hadoop.fs.BlockStoragePolicySpi;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -367,7 +368,7 @@ private List<String> readLines(File file) throws IOException {
     return lines;
   }
 
-  private static final class StreamPrinter extends Thread {
+  private static final class StreamPrinter extends SubjectInheritingThread {
     private final InputStream in;
     private final List<String> lines;
 
@@ -377,7 +378,7 @@ private StreamPrinter(InputStream in) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       try (BufferedReader br = new BufferedReader(
           new InputStreamReader(in, StandardCharsets.UTF_8))) {
         String line = br.readLine();
diff --git a/hadoop-tools/hadoop-distcp/src/test/java/org/apache/hadoop/tools/util/TestProducerConsumer.java b/hadoop-tools/hadoop-distcp/src/test/java/org/apache/hadoop/tools/util/TestProducerConsumer.java
index 9c461cb18fb..b77bf5afb97 100644
--- a/hadoop-tools/hadoop-distcp/src/test/java/org/apache/hadoop/tools/util/TestProducerConsumer.java
+++ b/hadoop-tools/hadoop-distcp/src/test/java/org/apache/hadoop/tools/util/TestProducerConsumer.java
@@ -23,6 +23,7 @@
 import org.apache.hadoop.tools.util.WorkReport;
 import org.apache.hadoop.tools.util.WorkRequest;
 import org.apache.hadoop.tools.util.WorkRequestProcessor;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
 
@@ -146,8 +147,8 @@ public void testMultipleProducerConsumerShutdown()
 
     // starts two thread: a source thread which put in work, and a sink thread
     // which takes a piece of work from ProducerConsumer
-    class SourceThread extends Thread {
-      public void run() {
+    class SourceThread extends SubjectInheritingThread {
+      public void work() {
         while (true) {
           try {
             worker.put(new WorkRequest<Integer>(42));
@@ -161,8 +162,8 @@ public void run() {
     // The source thread put requests into producer-consumer.
     SourceThread source = new SourceThread();
     source.start();
-    class SinkThread extends Thread {
-      public void run() {
+    class SinkThread extends SubjectInheritingThread {
+      public void work() {
         try {
           while (true) {
             WorkReport<Integer> report = worker.take();
diff --git a/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/ApplicationMaster.java b/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/ApplicationMaster.java
index e44f811f0db..f06195fccfb 100644
--- a/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/ApplicationMaster.java
+++ b/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/ApplicationMaster.java
@@ -19,6 +19,7 @@
 
 import org.apache.hadoop.thirdparty.com.google.common.base.Joiner;
 import org.apache.hadoop.util.Lists;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.thirdparty.com.google.common.primitives.Ints;
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -549,7 +550,7 @@ public void onContainersAllocated(List<Container> allocatedContainers) {
             + container.getNodeHttpAddress() + ", containerResourceMemory="
             + rsrc.getMemorySize() + ", containerResourceVirtualCores="
             + rsrc.getVirtualCores());
-        Thread launchThread = new Thread(containerLauncher);
+        Thread launchThread = new SubjectInheritingThread(containerLauncher);
 
         // launch and start the container on a separate thread to keep
         // the main thread unblocked
diff --git a/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/Client.java b/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/Client.java
index 0c57542747e..f6b2368dd2b 100644
--- a/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/Client.java
+++ b/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/Client.java
@@ -77,6 +77,7 @@
 import org.apache.hadoop.util.ClassUtil;
 import org.apache.hadoop.util.Tool;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationConstants;
 import org.apache.hadoop.yarn.api.ApplicationConstants.Environment;
 import org.apache.hadoop.yarn.api.protocolrecords.GetNewApplicationResponse;
@@ -891,7 +892,7 @@ private boolean monitorInfraApplication() throws YarnException, IOException {
     boolean loggedApplicationInfo = false;
     boolean success = false;
 
-    Thread namenodeMonitoringThread = new Thread(() -> {
+    Thread namenodeMonitoringThread = new SubjectInheritingThread(() -> {
       Supplier<Boolean> exitCritera = () ->
           Apps.isApplicationFinalState(infraAppState);
       Optional<Properties> namenodeProperties = Optional.empty();
diff --git a/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/DynoInfraUtils.java b/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/DynoInfraUtils.java
index f6c8a6ac4d5..f3779a33c8c 100644
--- a/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/DynoInfraUtils.java
+++ b/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/main/java/org/apache/hadoop/tools/dynamometer/DynoInfraUtils.java
@@ -52,6 +52,7 @@
 import org.apache.hadoop.net.NetUtils;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.YarnUncaughtExceptionHandler;
 import org.apache.hadoop.yarn.api.ApplicationConstants.Environment;
 
@@ -319,7 +320,7 @@ static void waitForNameNodeReadiness(final Properties nameNodeProperties,
           .get(getNameNodeHdfsUri(nameNodeProperties), conf);
       log.info("Launching thread to trigger block reports for Datanodes with <"
           + blockThreshold + " blocks reported");
-      Thread blockReportThread = new Thread(() -> {
+      Thread blockReportThread = new SubjectInheritingThread(() -> {
         // Here we count both Missing and UnderReplicated within under
         // replicated
         long lastUnderRepBlocks = Long.MAX_VALUE;
diff --git a/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/test/java/org/apache/hadoop/tools/dynamometer/TestDynamometerInfra.java b/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/test/java/org/apache/hadoop/tools/dynamometer/TestDynamometerInfra.java
index db34037da78..7326edb7f7d 100644
--- a/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/test/java/org/apache/hadoop/tools/dynamometer/TestDynamometerInfra.java
+++ b/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-infra/src/test/java/org/apache/hadoop/tools/dynamometer/TestDynamometerInfra.java
@@ -57,6 +57,7 @@
 import org.apache.hadoop.mapreduce.Counters;
 import org.apache.hadoop.util.JarFinder;
 import org.apache.hadoop.util.Shell;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ApplicationReport;
 import org.apache.hadoop.yarn.api.records.ContainerId;
@@ -461,7 +462,7 @@ private Client createAndStartClient(Configuration localConf) {
     final Client client = new Client(JarFinder.getJar(ApplicationMaster.class),
         JarFinder.getJar(Assertions.class));
     client.setConf(localConf);
-    Thread appThread = new Thread(() -> {
+    Thread appThread = new SubjectInheritingThread(() -> {
       try {
         client.run(new String[] {"-" + Client.MASTER_MEMORY_MB_ARG, "128",
             "-" + Client.CONF_PATH_ARG, confZip.toString(),
diff --git a/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-workload/src/main/java/org/apache/hadoop/tools/dynamometer/workloadgenerator/audit/AuditReplayThread.java b/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-workload/src/main/java/org/apache/hadoop/tools/dynamometer/workloadgenerator/audit/AuditReplayThread.java
index 14e8c9cb82f..fe8281e5da7 100644
--- a/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-workload/src/main/java/org/apache/hadoop/tools/dynamometer/workloadgenerator/audit/AuditReplayThread.java
+++ b/hadoop-tools/hadoop-dynamometer/hadoop-dynamometer-workload/src/main/java/org/apache/hadoop/tools/dynamometer/workloadgenerator/audit/AuditReplayThread.java
@@ -43,6 +43,7 @@
 
 import org.apache.hadoop.tools.dynamometer.workloadgenerator.audit.AuditReplayMapper.REPLAYCOUNTERS;
 import org.apache.hadoop.tools.dynamometer.workloadgenerator.audit.AuditReplayMapper.ReplayCommand;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -57,7 +58,7 @@
  * are inserted by the {@link AuditReplayMapper}. Once an item is ready, this
  * thread will fetch the command from the queue and attempt to replay it.
  */
-public class AuditReplayThread extends Thread {
+public class AuditReplayThread extends SubjectInheritingThread {
 
   private static final Logger LOG =
       LoggerFactory.getLogger(AuditReplayThread.class);
@@ -154,7 +155,7 @@ Exception getException() {
   }
 
   @Override
-  public void run() {
+  public void work() {
     long currentEpoch = System.currentTimeMillis();
     long delay = startTimestampMs - currentEpoch;
     try {
diff --git a/hadoop-tools/hadoop-federation-balance/src/main/java/org/apache/hadoop/tools/fedbalance/procedure/BalanceProcedureScheduler.java b/hadoop-tools/hadoop-federation-balance/src/main/java/org/apache/hadoop/tools/fedbalance/procedure/BalanceProcedureScheduler.java
index 4d4e9a26b3d..6eabff12d00 100644
--- a/hadoop-tools/hadoop-federation-balance/src/main/java/org/apache/hadoop/tools/fedbalance/procedure/BalanceProcedureScheduler.java
+++ b/hadoop-tools/hadoop-federation-balance/src/main/java/org/apache/hadoop/tools/fedbalance/procedure/BalanceProcedureScheduler.java
@@ -36,6 +36,7 @@
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -309,9 +310,9 @@ public void setJournal(BalanceJournal journal) {
   /**
    * This thread consumes the delayQueue and move the jobs to the runningQueue.
    */
-  class Rooster extends Thread {
+  class Rooster extends SubjectInheritingThread {
     @Override
-    public void run() {
+    public void work() {
       while (running.get()) {
         try {
           DelayWrapper dJob = delayQueue.take();
@@ -327,9 +328,9 @@ public void run() {
   /**
    * This thread consumes the runningQueue and give the job to the workers.
    */
-  class Reader extends Thread {
+  class Reader extends SubjectInheritingThread {
     @Override
-    public void run() {
+    public void work() {
       while (running.get()) {
         try {
           final BalanceJob job = runningQueue.poll(500, TimeUnit.MILLISECONDS);
@@ -361,9 +362,9 @@ public void run() {
    * This thread consumes the recoverQueue, recovers the job the adds it to the
    * runningQueue.
    */
-  class Recover extends Thread {
+  class Recover extends SubjectInheritingThread {
     @Override
-    public void run() {
+    public void work() {
       while (running.get()) {
         BalanceJob job = null;
         try {
diff --git a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/Gridmix.java b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/Gridmix.java
index e476223cf1e..82fed9d568b 100644
--- a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/Gridmix.java
+++ b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/Gridmix.java
@@ -43,6 +43,7 @@
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Tool;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.tools.rumen.JobStoryProducer;
 import org.apache.hadoop.tools.rumen.ZombieJobProducer;
 import org.slf4j.Logger;
@@ -627,7 +628,7 @@ private int setupDistCacheEmulation(Configuration conf, String traceIn,
    * pipeline abort its progress, waiting for each to exit and killing
    * any jobs still running on the cluster.
    */
-  class Shutdown extends Thread {
+  class Shutdown extends SubjectInheritingThread {
 
     static final long FAC_SLEEP = 1000;
     static final long SUB_SLEEP = 4000;
@@ -647,7 +648,7 @@ private void killComponent(Component<?> component, long maxwait) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       LOG.info("Exiting...");
       try {
         killComponent(factory, FAC_SLEEP);   // read no more tasks
diff --git a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/JobMonitor.java b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/JobMonitor.java
index 0b06911be08..cb3192dca5b 100644
--- a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/JobMonitor.java
+++ b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/JobMonitor.java
@@ -35,6 +35,7 @@
 import org.apache.hadoop.mapred.gridmix.Statistics.JobStats;
 import org.apache.hadoop.mapreduce.Job;
 import org.apache.hadoop.mapreduce.JobStatus;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 /**
  * Component accepting submitted, running {@link Statistics.JobStats} and 
@@ -133,14 +134,14 @@ List<JobStats> getRemainingJobs() {
    * Monitoring thread pulling running jobs from the component and into
    * a queue to be polled for status.
    */
-  private class MonitorThread extends Thread {
+  private class MonitorThread extends SubjectInheritingThread {
 
     public MonitorThread(int i) {
       super("GridmixJobMonitor-" + i);
     }
 
     @Override
-    public void run() {
+    public void work() {
       boolean graceful;
       boolean shutdown;
       while (true) {
diff --git a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/LoadJob.java b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/LoadJob.java
index d1229ce2d8f..bc98116e5b8 100644
--- a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/LoadJob.java
+++ b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/LoadJob.java
@@ -41,6 +41,7 @@
 import org.apache.hadoop.tools.rumen.JobStory;
 import org.apache.hadoop.tools.rumen.ResourceUsageMetrics;
 import org.apache.hadoop.tools.rumen.TaskInfo;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.util.ResourceCalculatorPlugin;
 
 import java.io.IOException;
@@ -143,7 +144,7 @@ private void configure() {
    * This is a progress based resource usage matcher.
    */
   @SuppressWarnings("unchecked")
-  static class ResourceUsageMatcherRunner extends Thread 
+  static class ResourceUsageMatcherRunner extends SubjectInheritingThread
   implements Progressive {
     private final ResourceUsageMatcher matcher;
     private final BoostingProgress progress;
@@ -199,7 +200,7 @@ protected void match() throws IOException, InterruptedException {
     }
     
     @Override
-    public void run() {
+    public void work() {
       LOG.info("Resource usage matcher thread started.");
       try {
         while (progress.getProgress() < 1) {
@@ -234,7 +235,7 @@ void boost(float value) {
   
   // Makes sure that the TaskTracker doesn't kill the map/reduce tasks while
   // they are emulating
-  private static class StatusReporter extends Thread {
+  private static class StatusReporter extends SubjectInheritingThread {
     private final TaskAttemptContext context;
     private final Progressive progress;
     
@@ -244,7 +245,7 @@ private static class StatusReporter extends Thread {
     }
     
     @Override
-    public void run() {
+    public void work() {
       LOG.info("Status reporter thread started.");
       try {
         while (!isInterrupted() && progress.getProgress() < 1) {
diff --git a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/ReplayJobFactory.java b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/ReplayJobFactory.java
index fe3b5d36d98..60572a515fb 100644
--- a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/ReplayJobFactory.java
+++ b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/ReplayJobFactory.java
@@ -22,6 +22,7 @@
 import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.tools.rumen.JobStory;
 import org.apache.hadoop.tools.rumen.JobStoryProducer;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -64,14 +65,14 @@ public Thread createReaderThread() {
    public void update(Statistics.ClusterStats item) {
    }
 
-   private class ReplayReaderThread extends Thread {
+   private class ReplayReaderThread extends SubjectInheritingThread {
 
     public ReplayReaderThread(String threadName) {
       super(threadName);
     }
 
 
-    public void run() {
+    public void work() {
       try {
         startFlag.await();
         if (Thread.currentThread().isInterrupted()) {
diff --git a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/SerialJobFactory.java b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/SerialJobFactory.java
index cb05ab63f1c..5f61d7e32ab 100644
--- a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/SerialJobFactory.java
+++ b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/SerialJobFactory.java
@@ -22,6 +22,7 @@
 import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.tools.rumen.JobStory;
 import org.apache.hadoop.tools.rumen.JobStoryProducer;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.mapred.gridmix.Statistics.JobStats;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.slf4j.Logger;
@@ -59,7 +60,7 @@ public Thread createReaderThread() {
     return new SerialReaderThread("SerialJobFactory");
   }
 
-  private class SerialReaderThread extends Thread {
+  private class SerialReaderThread extends SubjectInheritingThread {
 
     public SerialReaderThread(String threadName) {
       super(threadName);
@@ -78,7 +79,7 @@ public SerialReaderThread(String threadName) {
      * ==
      */
     @Override
-    public void run() {
+    public void work() {
       try {
         startFlag.await();
         if (Thread.currentThread().isInterrupted()) {
diff --git a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/Statistics.java b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/Statistics.java
index bf73f2a1faa..9178fdb85cb 100644
--- a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/Statistics.java
+++ b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/Statistics.java
@@ -28,6 +28,7 @@
 import org.apache.hadoop.mapreduce.JobStatus;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.tools.rumen.JobStory;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import java.io.IOException;
 import java.security.PrivilegedExceptionAction;
@@ -216,13 +217,13 @@ public void start() {
     statistics.start();
   }
 
-  private class StatCollector extends Thread {
+  private class StatCollector extends SubjectInheritingThread {
 
     StatCollector() {
       super("StatsCollectorThread");
     }
 
-    public void run() {
+    public void work() {
       try {
         startFlag.await();
         if (Thread.currentThread().isInterrupted()) {
diff --git a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/StressJobFactory.java b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/StressJobFactory.java
index 4e7fc9c2bbd..6e9c04a47b5 100644
--- a/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/StressJobFactory.java
+++ b/hadoop-tools/hadoop-gridmix/src/main/java/org/apache/hadoop/mapred/gridmix/StressJobFactory.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.tools.rumen.JobStory;
 import org.apache.hadoop.tools.rumen.JobStoryProducer;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import java.io.IOException;
 import java.util.HashSet;
@@ -136,7 +137,7 @@ public Thread createReaderThread() {
   * Worker thread responsible for reading descriptions, assigning sequence
   * numbers, and normalizing time.
   */
-  private class StressReaderThread extends Thread {
+  private class StressReaderThread extends SubjectInheritingThread {
 
     public StressReaderThread(String name) {
       super(name);
@@ -152,7 +153,7 @@ public StressReaderThread(String name) {
      * load the JT.
      * That is submit  (Sigma(no of maps/Job)) > (2 * no of slots available)
      */
-    public void run() {
+    public void work() {
       try {
         startFlag.await();
         if (Thread.currentThread().isInterrupted()) {
diff --git a/hadoop-tools/hadoop-resourceestimator/src/main/java/org/apache/hadoop/resourceestimator/service/ShutdownHook.java b/hadoop-tools/hadoop-resourceestimator/src/main/java/org/apache/hadoop/resourceestimator/service/ShutdownHook.java
index 23e1413fcad..4d2646fb84c 100644
--- a/hadoop-tools/hadoop-resourceestimator/src/main/java/org/apache/hadoop/resourceestimator/service/ShutdownHook.java
+++ b/hadoop-tools/hadoop-resourceestimator/src/main/java/org/apache/hadoop/resourceestimator/service/ShutdownHook.java
@@ -20,13 +20,14 @@
 
 package org.apache.hadoop.resourceestimator.service;
 
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * Simple shutdown hook for {@link ResourceEstimatorServer}.
  */
-public class ShutdownHook extends Thread {
+public class ShutdownHook extends SubjectInheritingThread {
   private static final Logger LOGGER =
       LoggerFactory.getLogger(ShutdownHook.class);
   private final ResourceEstimatorServer server;
@@ -35,7 +36,7 @@ public class ShutdownHook extends Thread {
     this.server = server;
   }
 
-  public void run() {
+  public void work() {
     try {
       server.shutdown();
     } catch (Exception e) {
diff --git a/hadoop-tools/hadoop-streaming/src/main/java/org/apache/hadoop/streaming/PipeMapRed.java b/hadoop-tools/hadoop-streaming/src/main/java/org/apache/hadoop/streaming/PipeMapRed.java
index 3d6541565cb..d3a2287fb1c 100644
--- a/hadoop-tools/hadoop-streaming/src/main/java/org/apache/hadoop/streaming/PipeMapRed.java
+++ b/hadoop-tools/hadoop-streaming/src/main/java/org/apache/hadoop/streaming/PipeMapRed.java
@@ -38,6 +38,7 @@
 import org.apache.hadoop.streaming.io.TextOutputReader;
 import org.apache.hadoop.util.LineReader;
 import org.apache.hadoop.util.ReflectionUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.apache.hadoop.io.Text;
 
@@ -366,7 +367,7 @@ OutputReader createOutputReader(Class<? extends OutputReader> outputReaderClass)
   }
   
   
-  class MROutputThread extends Thread {
+  class MROutputThread extends SubjectInheritingThread {
 
     MROutputThread(OutputReader outReader, OutputCollector outCollector,
       Reporter reporter) {
@@ -376,7 +377,7 @@ class MROutputThread extends Thread {
       this.reporter = reporter;
     }
 
-    public void run() {
+    public void work() {
       try {
         // 3/4 Tool to Hadoop
         while (outReader.readKeyValue()) {
@@ -418,7 +419,7 @@ public void run() {
     
   }
 
-  class MRErrorThread extends Thread {
+  class MRErrorThread extends SubjectInheritingThread {
 
     public MRErrorThread() {
       this.reporterPrefix = job_.get("stream.stderr.reporter.prefix", "reporter:");
@@ -431,7 +432,7 @@ public void setReporter(Reporter reporter) {
       this.reporter = reporter;
     }
       
-    public void run() {
+    public void work() {
       Text line = new Text();
       LineReader lineReader = null;
       try {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/main/java/org/apache/hadoop/yarn/applications/distributedshell/ApplicationMaster.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/main/java/org/apache/hadoop/yarn/applications/distributedshell/ApplicationMaster.java
index d6ec1e4d4c5..dad39209514 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/main/java/org/apache/hadoop/yarn/applications/distributedshell/ApplicationMaster.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/main/java/org/apache/hadoop/yarn/applications/distributedshell/ApplicationMaster.java
@@ -69,6 +69,7 @@
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.util.ExitUtil;
 import org.apache.hadoop.util.Shell;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationConstants;
 import org.apache.hadoop.yarn.api.ApplicationConstants.Environment;
 import org.apache.hadoop.yarn.api.ApplicationMasterProtocol;
@@ -1761,7 +1762,7 @@ Thread createLaunchContainerThread(Container allocatedContainer,
     LaunchContainerRunnable runnableLaunchContainer =
         new LaunchContainerRunnable(allocatedContainer, containerListener,
             shellId);
-    return new Thread(runnableLaunchContainer);
+    return new SubjectInheritingThread(runnableLaunchContainer);
   }
 
   private void publishContainerStartEventOnTimelineServiceV2(
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/DistributedShellBaseTest.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/DistributedShellBaseTest.java
index 607a4c90d7e..34d4273a089 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/DistributedShellBaseTest.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/DistributedShellBaseTest.java
@@ -53,6 +53,7 @@
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.JarFinder;
 import org.apache.hadoop.util.Shell;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptReport;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
@@ -333,7 +334,7 @@ protected void baseTestDSShell(String methodName, boolean haveDomain, boolean de
     assertTrue(initSuccess);
     LOG.info("Running DS Client");
     final AtomicBoolean result = new AtomicBoolean(false);
-    Thread t = new Thread(() -> {
+    Thread t = new SubjectInheritingThread(() -> {
       try {
         result.set(dsClient.run());
       } catch (Exception e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSAppMaster.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSAppMaster.java
index 81420465afb..62defa28bbf 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSAppMaster.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSAppMaster.java
@@ -20,6 +20,7 @@
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.Container;
@@ -69,7 +70,7 @@ protected Thread createLaunchContainerThread(Container allocatedContainer,
       threadsLaunched++;
       launchedContainers.add(allocatedContainer.getId());
       yarnShellIds.add(shellId);
-      return new Thread();
+      return new SubjectInheritingThread();
     }
 
     void setNumTotalContainers(int numTotalContainers) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSTimelineV20.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSTimelineV20.java
index 6ef26ed1cce..f75808694b8 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSTimelineV20.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSTimelineV20.java
@@ -35,6 +35,7 @@
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptReport;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
@@ -126,7 +127,7 @@ public void testDSShellWithEnforceExecutionType(TestInfo testInfo) throws Except
     try {
       setAndGetDSClient(new Configuration(getYarnClusterConfiguration()));
       getDSClient().init(args);
-      Thread dsClientRunner = new Thread(() -> {
+      Thread dsClientRunner = new SubjectInheritingThread(() -> {
         try {
           getDSClient().run();
         } catch (Exception e) {
@@ -220,7 +221,7 @@ private void doTestDistributedShellWithResources(
     assertTrue(getDSClient().init(args));
     LOG.info("Running DS Client");
     final AtomicBoolean result = new AtomicBoolean(false);
-    Thread dsClientRunner = new Thread(() -> {
+    Thread dsClientRunner = new SubjectInheritingThread(() -> {
       try {
         result.set(getDSClient().run());
       } catch (Exception e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSWithMultipleNodeManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSWithMultipleNodeManager.java
index 7ebc747ed2e..f38b5044208 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSWithMultipleNodeManager.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-distributedshell/src/test/java/org/apache/hadoop/yarn/applications/distributedshell/TestDSWithMultipleNodeManager.java
@@ -40,6 +40,7 @@
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableMap;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.NodeId;
 import org.apache.hadoop.yarn.client.api.YarnClient;
@@ -320,7 +321,7 @@ public void testDistributedShellWithAllocationTagNamespace(
           new Client(
               new Configuration(distShellTest.getYarnClusterConfiguration()));
       dsClient.init(argsA);
-      Thread dsClientRunner = new Thread(() -> {
+      Thread dsClientRunner = new SubjectInheritingThread(() -> {
         try {
           dsClient.run();
         } catch (Exception e) {
@@ -455,7 +456,7 @@ private void waitForExpectedNMsCount(int[] expectedNMCounts,
   /**
    * Monitor containers running on NMs.
    */
-  class NMContainerMonitor extends Thread {
+  class NMContainerMonitor extends SubjectInheritingThread {
     // The interval of milliseconds of sampling (500ms)
     private final static int SAMPLING_INTERVAL_MS = 500;
 
@@ -465,7 +466,7 @@ class NMContainerMonitor extends Thread {
     private volatile boolean isRunning = true;
 
     @Override
-    public void run() {
+    public void work() {
       while (isRunning) {
         for (int i = 0; i < NUM_NMS; i++) {
           int nContainers =
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/main/java/org/apache/hadoop/yarn/applications/unmanagedamlauncher/UnmanagedAMLauncher.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/main/java/org/apache/hadoop/yarn/applications/unmanagedamlauncher/UnmanagedAMLauncher.java
index 37b3477c8c1..7e7af2ccd12 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/main/java/org/apache/hadoop/yarn/applications/unmanagedamlauncher/UnmanagedAMLauncher.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-unmanaged-am-launcher/src/main/java/org/apache/hadoop/yarn/applications/unmanagedamlauncher/UnmanagedAMLauncher.java
@@ -40,6 +40,7 @@
 import org.apache.hadoop.fs.FileUtil;
 import org.apache.hadoop.security.Credentials;
 import org.apache.hadoop.security.token.Token;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationConstants;
 import org.apache.hadoop.yarn.api.ApplicationConstants.Environment;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
@@ -242,9 +243,9 @@ public void launchAM(ApplicationAttemptId attemptId)
     
     // read error and input streams as this would free up the buffers
     // free the error stream buffer
-    Thread errThread = new Thread() {
+    Thread errThread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           String line = errReader.readLine();
           while((line != null) && !isInterrupted()) {
@@ -256,9 +257,9 @@ public void run() {
         }
       }
     };
-    Thread outThread = new Thread() {
+    Thread outThread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           String line = inReader.readLine();
           while((line != null) && !isInterrupted()) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/client/SystemServiceManagerImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/client/SystemServiceManagerImpl.java
index f971d7140aa..fcb82173602 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/client/SystemServiceManagerImpl.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/client/SystemServiceManagerImpl.java
@@ -24,6 +24,7 @@
 import org.apache.hadoop.fs.RemoteIterator;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.service.AbstractService;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.exceptions.YarnException;
 import org.apache.hadoop.yarn.server.service.SystemServiceManager;
@@ -127,7 +128,7 @@ protected void serviceStart() throws Exception {
     launchUserService(syncUserServices);
     // Create a thread and submit services in background otherwise it
     // block RM switch time.
-    serviceLaucher = new Thread(createRunnable());
+    serviceLaucher = new SubjectInheritingThread(createRunnable());
     serviceLaucher.setName("System service launcher");
     serviceLaucher.start();
   }
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/ClientAMService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/ClientAMService.java
index 2a9bf8d5d97..168d24fb36b 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/ClientAMService.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/ClientAMService.java
@@ -26,6 +26,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.ExitUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationConstants;
 import org.apache.hadoop.yarn.api.records.ContainerId;
 import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
@@ -154,9 +155,9 @@ public StopResponseProto stop(StopRequestProto requestProto)
 
     // Stop the service in 2 seconds delay to make sure this rpc call is completed.
     // shutdown hook will be executed which will stop AM gracefully.
-    Thread thread = new Thread() {
+    Thread thread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           Thread.sleep(2000);
           ExitUtil.terminate(0);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/utils/TestServiceApiUtil.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/utils/TestServiceApiUtil.java
index 0c0a53fecd4..27440428838 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/utils/TestServiceApiUtil.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/utils/TestServiceApiUtil.java
@@ -19,6 +19,7 @@
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.registry.client.api.RegistryConstants;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.service.ServiceTestUtils;
 import org.apache.hadoop.yarn.service.api.records.Artifact;
@@ -725,9 +726,9 @@ public void testNoServiceDependencies() {
 
   @Test
   public void testServiceDependencies() {
-    Thread thread = new Thread() {
+    SubjectInheritingThread thread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         Service service = createExampleApplication();
         Component compa = createComponent("compa");
         Component compb = createComponent("compb");
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/ContainerShellWebSocket.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/ContainerShellWebSocket.java
index 5656484fca1..34097ab545b 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/ContainerShellWebSocket.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/ContainerShellWebSocket.java
@@ -24,6 +24,7 @@
 
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
@@ -85,7 +86,7 @@ public void onClose(Session session, int status, String reason) {
   public void run() {
     try {
       Reader consoleReader = new Reader();
-      Thread inputThread = new Thread(consoleReader, "consoleReader");
+      Thread inputThread = new SubjectInheritingThread(consoleReader, "consoleReader");
       inputThread.start();
       while (mySession.isOpen()) {
         mySession.getRemote().flush();
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/async/impl/AMRMClientAsyncImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/async/impl/AMRMClientAsyncImpl.java
index 376c9dc1b05..7a456036345 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/async/impl/AMRMClientAsyncImpl.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/async/impl/AMRMClientAsyncImpl.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.classification.InterfaceAudience.Private;
 import org.apache.hadoop.classification.InterfaceStability.Unstable;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
 import org.apache.hadoop.yarn.api.protocolrecords.RegisterApplicationMasterResponse;
 import org.apache.hadoop.yarn.api.records.Container;
@@ -293,12 +294,12 @@ public void updateTrackingUrl(String trackingUrl) {
     client.updateTrackingUrl(trackingUrl);
   }
   
-  private class HeartbeatThread extends Thread {
+  private class HeartbeatThread extends SubjectInheritingThread {
     public HeartbeatThread() {
       super("AMRM Heartbeater thread");
     }
     
-    public void run() {
+    public void work() {
       while (true) {
         Object response = null;
         // synchronization ensures we don't send heartbeats after unregistering
@@ -337,12 +338,12 @@ public void run() {
     }
   }
   
-  private class CallbackHandlerThread extends Thread {
+  private class CallbackHandlerThread extends SubjectInheritingThread {
     public CallbackHandlerThread() {
       super("AMRM Callback Handler Thread");
     }
     
-    public void run() {
+    public void work() {
       while (true) {
         if (!keepRunning) {
           return;
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/async/impl/NMClientAsyncImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/async/impl/NMClientAsyncImpl.java
index 4a4c50607da..91650f84e93 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/async/impl/NMClientAsyncImpl.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/async/impl/NMClientAsyncImpl.java
@@ -59,6 +59,7 @@
 
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -73,7 +74,7 @@ public class NMClientAsyncImpl extends NMClientAsync {
 
   protected ThreadPoolExecutor threadPool;
   protected int maxThreadPoolSize;
-  protected Thread eventDispatcherThread;
+  protected SubjectInheritingThread eventDispatcherThread;
   protected AtomicBoolean stopped = new AtomicBoolean(false);
   protected BlockingQueue<ContainerEvent> events =
       new LinkedBlockingQueue<ContainerEvent>();
@@ -151,9 +152,9 @@ protected void serviceStart() throws Exception {
     threadPool = new ThreadPoolExecutor(initSize, Integer.MAX_VALUE, 1,
         TimeUnit.HOURS, new LinkedBlockingQueue<Runnable>(), tf);
 
-    eventDispatcherThread = new Thread() {
+    eventDispatcherThread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         ContainerEvent event = null;
         Set<String> allNodes = new HashSet<String>();
 
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/TopCLI.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/TopCLI.java
index c16fe03b82a..7ba4dfaa2cf 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/TopCLI.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/TopCLI.java
@@ -63,6 +63,7 @@
 import org.apache.hadoop.security.ssl.SSLFactory;
 import org.apache.hadoop.util.Time;
 import org.apache.hadoop.util.ToolRunner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationsRequest;
 import org.apache.hadoop.yarn.api.records.ApplicationReport;
 import org.apache.hadoop.yarn.api.records.Priority;
@@ -367,9 +368,9 @@ private static class QueueMetrics {
     long pendingContainers;
   }
 
-  private class KeyboardMonitor extends Thread {
+  private class KeyboardMonitor extends SubjectInheritingThread {
 
-    public void run() {
+    public void work() {
       Scanner keyboard = new Scanner(System.in, "UTF-8");
       while (runKeyboardMonitor.get()) {
         String in = keyboard.next();
@@ -1229,7 +1230,7 @@ private String getCommandOutput(String[] command) throws IOException,
 
   private void addShutdownHook() {
     //clear screen when the program exits
-    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+    Runtime.getRuntime().addShutdownHook(new SubjectInheritingThread(() -> {
       clearScreen();
     }));
   }
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/ProtocolHATestBase.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/ProtocolHATestBase.java
index eff68990ea8..4335b39f86f 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/ProtocolHATestBase.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/ProtocolHATestBase.java
@@ -23,6 +23,7 @@
 import java.util.Map;
 import java.util.concurrent.TimeoutException;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.protocolrecords.FinishApplicationMasterRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.FinishApplicationMasterResponse;
 import org.apache.hadoop.yarn.api.protocolrecords.RegisterApplicationMasterRequest;
@@ -244,8 +245,8 @@ protected void verifyClientConnection() throws InterruptedException {
   }
 
   protected Thread createAndStartFailoverThread() {
-    Thread failoverThread = new Thread() {
-      public void run() {
+    SubjectInheritingThread failoverThread = new SubjectInheritingThread() {
+      public void work() {
         keepRunning = true;
         while (keepRunning) {
           if (cluster.getStartFailoverFlag()) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestFederationRMFailoverProxyProvider.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestFederationRMFailoverProxyProvider.java
index 165569df4c7..b7ab17485d8 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestFederationRMFailoverProxyProvider.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestFederationRMFailoverProxyProvider.java
@@ -25,6 +25,7 @@
 import org.apache.hadoop.ha.HAServiceProtocol;
 import org.apache.hadoop.io.retry.FailoverProxyProvider.ProxyInfo;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationClientProtocol;
 import org.apache.hadoop.yarn.api.ApplicationMasterProtocol;
 import org.apache.hadoop.yarn.api.protocolrecords.GetClusterMetricsRequest;
@@ -160,7 +161,7 @@ private void testProxyProvider(boolean facadeFlushCache) throws Exception {
         .getSubClusters(any(GetSubClustersInfoRequest.class));
 
     threadResponse = null;
-    Thread thread = new Thread(new Runnable() {
+    Thread thread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestGetGroups.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestGetGroups.java
index 38f220dea8c..01540c58432 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestGetGroups.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestGetGroups.java
@@ -29,6 +29,7 @@
 import org.apache.hadoop.service.ServiceStateChangeListener;
 import org.apache.hadoop.tools.GetGroupsTestBase;
 import org.apache.hadoop.util.Tool;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
 import org.junit.jupiter.api.AfterAll;
@@ -70,8 +71,8 @@ public void stateChanged(Service service) {
     resourceManager.registerServiceListener(rmStateChangeListener);
 
     resourceManager.init(conf);
-    new Thread() {
-      public void run() {
+    new SubjectInheritingThread() {
+      public void work() {
         resourceManager.start();
       };
     }.start();
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestHedgingRequestRMFailoverProxyProvider.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestHedgingRequestRMFailoverProxyProvider.java
index f0aedf622ec..72cd5104ef3 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestHedgingRequestRMFailoverProxyProvider.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestHedgingRequestRMFailoverProxyProvider.java
@@ -22,6 +22,7 @@
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.ha.HAServiceProtocol;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.client.api.YarnClient;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.exceptions.ApplicationNotFoundException;
@@ -107,8 +108,8 @@ private void validateActiveRM(YarnClient client) throws IOException {
   }
 
   private void makeRMActive(final MiniYARNCluster cluster, final int index) {
-    Thread t = new Thread() {
-      @Override public void run() {
+    SubjectInheritingThread t = new SubjectInheritingThread() {
+      @Override public void work() {
         try {
           System.out.println("Transition rm" + index + " to active");
           cluster.getResourceManager(index).getRMContext().getRMAdminService()
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestRMFailover.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestRMFailover.java
index 963d01b4c90..fc7cc9f2c85 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestRMFailover.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestRMFailover.java
@@ -41,6 +41,7 @@
 import org.apache.hadoop.service.Service.STATE;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.ExitUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.client.api.YarnClient;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
@@ -402,7 +403,7 @@ public void testUncaughtExceptionHandlerWithHAEnabled()
 
     // Create a thread and throw a RTE inside it
     final RuntimeException rte = new RuntimeException("TestRuntimeException");
-    final Thread testThread = new Thread(new Runnable() {
+    final Thread testThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         throw rte;
@@ -446,7 +447,7 @@ public void testUncaughtExceptionHandlerWithoutHA()
 
     // Create a thread and throw a RTE inside it
     final RuntimeException rte = new RuntimeException("TestRuntimeException");
-    final Thread testThread = new Thread(new Runnable() {
+    final Thread testThread = new SubjectInheritingThread(new Runnable() {
       @Override public void run() {
         throw rte;
       }
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestResourceManagerAdministrationProtocolPBClientImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestResourceManagerAdministrationProtocolPBClientImpl.java
index dfdd8aa53fa..03989ccd67b 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestResourceManagerAdministrationProtocolPBClientImpl.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/TestResourceManagerAdministrationProtocolPBClientImpl.java
@@ -26,6 +26,7 @@
 import org.apache.hadoop.service.Service;
 import org.apache.hadoop.service.Service.STATE;
 import org.apache.hadoop.service.ServiceStateChangeListener;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.DecommissionType;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.factories.RecordFactory;
@@ -97,8 +98,8 @@ public void stateChanged(Service service) {
     resourceManager.registerServiceListener(rmStateChangeListener);
 
     resourceManager.init(configuration);
-    new Thread() {
-      public void run() {
+    new SubjectInheritingThread() {
+      public void work() {
         resourceManager.start();
       }
     }.start();
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/async/impl/TestNMClientAsync.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/async/impl/TestNMClientAsync.java
index 1a8b8f50403..408ed7517a7 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/async/impl/TestNMClientAsync.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/async/impl/TestNMClientAsync.java
@@ -47,6 +47,7 @@
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.service.ServiceOperations;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.Container;
@@ -782,9 +783,9 @@ public void testOutOfOrder() throws Exception {
         recordFactory.newRecordInstance(ContainerLaunchContext.class);
 
     // start container from another thread
-    Thread t = new Thread() {
+    SubjectInheritingThread t = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         asyncClient.startContainerAsync(container, clc);
       }
     };
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestYarnClient.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestYarnClient.java
index f0da7713321..3e00c1647c2 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestYarnClient.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/api/impl/TestYarnClient.java
@@ -23,6 +23,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationClientProtocol;
 import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationAttemptReportRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationAttemptReportResponse;
@@ -291,10 +292,10 @@ public void testSubmitApplicationInterrupted(SchedulerType type) throws IOExcept
       client.start();
       // Submit the application and then interrupt it while its waiting
       // for submission to be successful.
-      final class SubmitThread extends Thread {
+      final class SubmitThread extends SubjectInheritingThread {
         private boolean isInterrupted  = false;
         @Override
-        public void run() {
+        public void work() {
           ApplicationSubmissionContext context =
               mock(ApplicationSubmissionContext.class);
           ApplicationId applicationId = ApplicationId.newInstance(
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/AsyncDispatcher.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/AsyncDispatcher.java
index a3436f7bbe0..7be22ad4d4a 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/AsyncDispatcher.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/AsyncDispatcher.java
@@ -42,6 +42,7 @@
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.ShutdownHookManager;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
 
@@ -218,7 +219,7 @@ protected void serviceInit(Configuration conf) throws Exception{
   protected void serviceStart() throws Exception {
     //start all the components
     super.serviceStart();
-    eventHandlingThread = new Thread(createThread());
+    eventHandlingThread = new SubjectInheritingThread(createThread());
     eventHandlingThread.setName(dispatcherThreadName);
     eventHandlingThread.start();
   }
@@ -284,7 +285,7 @@ protected void dispatch(Event event) {
           && (ShutdownHookManager.get().isShutdownInProgress()) == false
           && stopped == false) {
         stopped = true;
-        Thread shutDownThread = new Thread(createShutDownThread());
+        Thread shutDownThread = new SubjectInheritingThread(createShutDownThread());
         shutDownThread.setName("AsyncDispatcher ShutDown handler");
         shutDownThread.start();
       }
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/EventDispatcher.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/EventDispatcher.java
index 647ab6e9481..a5f756ecc84 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/EventDispatcher.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/event/EventDispatcher.java
@@ -28,6 +28,7 @@
 import org.slf4j.MarkerFactory;
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.ShutdownHookManager;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
 
 import java.util.concurrent.BlockingQueue;
@@ -105,7 +106,7 @@ public void run() {
   public EventDispatcher(EventHandler<T> handler, String name) {
     super(name);
     this.handler = handler;
-    this.eventProcessor = new Thread(new EventProcessor());
+    this.eventProcessor = new SubjectInheritingThread(new EventProcessor());
     this.eventProcessor.setName(getName() + ":Event Processor");
   }
 
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/AbstractLivelinessMonitor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/AbstractLivelinessMonitor.java
index 0ae6c47d0ec..944baebfaaa 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/AbstractLivelinessMonitor.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/AbstractLivelinessMonitor.java
@@ -27,6 +27,7 @@
 import org.apache.hadoop.classification.InterfaceAudience.Public;
 import org.apache.hadoop.classification.InterfaceStability.Evolving;
 import org.apache.hadoop.service.AbstractService;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 /**
  * A simple liveliness monitor with which clients can register, trust the
@@ -66,7 +67,7 @@ public AbstractLivelinessMonitor(String name) {
   protected void serviceStart() throws Exception {
     assert !stopped : "starting when already stopped";
     resetTimer();
-    checkerThread = new Thread(new PingChecker());
+    checkerThread = new SubjectInheritingThread(new PingChecker());
     checkerThread.setName("Ping Checker for "+getName());
     checkerThread.start();
     super.serviceStart();
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/TestYarnUncaughtExceptionHandler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/TestYarnUncaughtExceptionHandler.java
index e0201cfcd15..d2d2f598255 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/TestYarnUncaughtExceptionHandler.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/TestYarnUncaughtExceptionHandler.java
@@ -21,6 +21,7 @@
 import org.junit.jupiter.api.Test;
 
 import org.apache.hadoop.util.ExitUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
 
 import static org.junit.jupiter.api.Assertions.assertSame;
@@ -44,7 +45,7 @@ void testUncaughtExceptionHandlerWithRuntimeException()
     final YarnUncaughtExceptionHandler spyYarnHandler = spy(exHandler);
     final YarnRuntimeException yarnException = new YarnRuntimeException(
         "test-yarn-runtime-exception");
-    final Thread yarnThread = new Thread(new Runnable() {
+    final Thread yarnThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         throw yarnException;
@@ -74,7 +75,7 @@ void testUncaughtExceptionHandlerWithError()
     ExitUtil.disableSystemExit();
     final YarnUncaughtExceptionHandler spyErrorHandler = spy(exHandler);
     final java.lang.Error error = new java.lang.Error("test-error");
-    final Thread errorThread = new Thread(new Runnable() {
+    final Thread errorThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         throw error;
@@ -103,7 +104,7 @@ void testUncaughtExceptionHandlerWithOutOfMemoryError()
     ExitUtil.disableSystemHalt();
     final YarnUncaughtExceptionHandler spyOomHandler = spy(exHandler);
     final OutOfMemoryError oomError = new OutOfMemoryError("out-of-memory-error");
-    final Thread oomThread = new Thread(new Runnable() {
+    final Thread oomThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         throw oomError;
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/logaggregation/TestAggregatedLogFormat.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/logaggregation/TestAggregatedLogFormat.java
index 054e751ff64..9fffdf60df8 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/logaggregation/TestAggregatedLogFormat.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/logaggregation/TestAggregatedLogFormat.java
@@ -55,6 +55,7 @@
 import org.apache.hadoop.io.nativeio.NativeIO;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.TestContainerId;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
@@ -174,8 +175,8 @@ private void writeSrcFileAndALog(Path srcFilePath, String fileName, final long l
 
       final CountDownLatch latch = new CountDownLatch(1);
 
-      Thread t = new Thread() {
-        public void run() {
+      SubjectInheritingThread t = new SubjectInheritingThread() {
+        public void work() {
           try {
             for (int i = 0; i < length / 3; i++) {
               osw.write(ch);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestProcfsBasedProcessTree.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestProcfsBasedProcessTree.java
index 09dfb92f1d0..556598eb195 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestProcfsBasedProcessTree.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestProcfsBasedProcessTree.java
@@ -47,6 +47,7 @@
 import org.apache.hadoop.util.Shell.ExitCodeException;
 import org.apache.hadoop.util.Shell.ShellCommandExecutor;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.util.ProcfsBasedProcessTree.MemInfo;
 import org.apache.hadoop.yarn.util.ProcfsBasedProcessTree.ProcessSmapMemoryInfo;
@@ -75,8 +76,8 @@ public class TestProcfsBasedProcessTree {
 
   private static final int N = 6; // Controls the RogueTask
 
-  private class RogueTaskThread extends Thread {
-    public void run() {
+  private class RogueTaskThread extends SubjectInheritingThread {
+    public void work() {
       try {
         Vector<String> args = new Vector<String>();
         if (isSetsidAvailable()) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/LeveldbTimelineStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/LeveldbTimelineStore.java
index 78741720a17..8089c0facea 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/LeveldbTimelineStore.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/LeveldbTimelineStore.java
@@ -20,6 +20,7 @@
 
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.util.Preconditions;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.commons.collections4.map.LRUMap;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.classification.InterfaceAudience.Private;
@@ -283,7 +284,7 @@ public StartAndInsertTime(long startTime, long insertTime) {
     }
   }
 
-  private class EntityDeletionThread extends Thread {
+  private class EntityDeletionThread extends SubjectInheritingThread {
     private final long ttl;
     private final long ttlInterval;
 
@@ -298,7 +299,7 @@ public EntityDeletionThread(Configuration conf) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       while (true) {
         long timestamp = System.currentTimeMillis() - ttl;
         try {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/RollingLevelDBTimelineStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/RollingLevelDBTimelineStore.java
index 97ff86ede27..1d52a74c4ac 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/RollingLevelDBTimelineStore.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/RollingLevelDBTimelineStore.java
@@ -20,6 +20,7 @@
 
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.util.Preconditions;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -389,7 +390,7 @@ protected void serviceStop() throws Exception {
     super.serviceStop();
   }
 
-  private class EntityDeletionThread extends Thread {
+  private class EntityDeletionThread extends SubjectInheritingThread {
     private final long ttl;
     private final long ttlInterval;
 
@@ -404,7 +405,7 @@ private class EntityDeletionThread extends Thread {
     }
 
     @Override
-    public void run() {
+    public void work() {
       Thread.currentThread().setName("Leveldb Timeline Store Retention");
       while (true) {
         long timestamp = System.currentTimeMillis() - ttl;
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/main/java/org/apache/hadoop/yarn/server/AMHeartbeatRequestHandler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/main/java/org/apache/hadoop/yarn/server/AMHeartbeatRequestHandler.java
index cb59d41505d..0d6a4d188be 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/main/java/org/apache/hadoop/yarn/server/AMHeartbeatRequestHandler.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/main/java/org/apache/hadoop/yarn/server/AMHeartbeatRequestHandler.java
@@ -34,13 +34,14 @@
 
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.util.Preconditions;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 /**
  * Extends Thread and provides an implementation that is used for processing the
  * AM heart beat request asynchronously and sending back the response using the
  * callback method registered with the system.
  */
-public class AMHeartbeatRequestHandler extends Thread {
+public class AMHeartbeatRequestHandler extends SubjectInheritingThread {
   public static final Logger LOG =
       LoggerFactory.getLogger(AMHeartbeatRequestHandler.class);
 
@@ -83,7 +84,7 @@ public void shutdown() {
   }
 
   @Override
-  public void run() {
+  public void work() {
     while (keepRunning) {
       AsyncAllocateRequestInfo requestInfo;
       try {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/main/java/org/apache/hadoop/yarn/server/uam/UnmanagedAMPoolManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/main/java/org/apache/hadoop/yarn/server/uam/UnmanagedAMPoolManager.java
index 0ff4260c5e3..ecad69c6fd3 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/main/java/org/apache/hadoop/yarn/server/uam/UnmanagedAMPoolManager.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/main/java/org/apache/hadoop/yarn/server/uam/UnmanagedAMPoolManager.java
@@ -37,6 +37,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.service.AbstractService;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationClientProtocol;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
@@ -105,7 +106,7 @@ protected void serviceStart() throws Exception {
   protected void serviceStop() throws Exception {
 
     if (!this.unmanagedAppMasterMap.isEmpty()) {
-      finishApplicationThread = new Thread(createForceFinishApplicationThread());
+      finishApplicationThread = new SubjectInheritingThread(createForceFinishApplicationThread());
       finishApplicationThread.setName(dispatcherThreadName);
       finishApplicationThread.start();
     }
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/java/org/apache/hadoop/yarn/server/uam/TestUnmanagedApplicationManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/java/org/apache/hadoop/yarn/server/uam/TestUnmanagedApplicationManager.java
index bb6944e1034..80b9c7d9b69 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/java/org/apache/hadoop/yarn/server/uam/TestUnmanagedApplicationManager.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/src/test/java/org/apache/hadoop/yarn/server/uam/TestUnmanagedApplicationManager.java
@@ -33,6 +33,7 @@
 import org.apache.hadoop.service.Service;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateResponse;
 import org.apache.hadoop.yarn.api.protocolrecords.FinishApplicationMasterRequest;
@@ -228,7 +229,7 @@ public void testSlowRegisterCall()
       throws YarnException, IOException, InterruptedException {
 
     // Register with wait() in RM in a separate thread
-    Thread registerAMThread = new Thread(new Runnable() {
+    Thread registerAMThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
@@ -486,10 +487,10 @@ public TestableAMRequestHandlerThread(Configuration conf,
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         getUGIWithToken(attemptId).doAs((PrivilegedExceptionAction<Object>) () -> {
-          TestableAMRequestHandlerThread.super.run();
+          TestableAMRequestHandlerThread.super.work();
           return null;
         });
       } catch (Exception e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java
index 76d3439575c..ad34ccc329c 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/ContainerExecutor.java
@@ -68,6 +68,7 @@
 import org.apache.hadoop.yarn.server.nodemanager.util.ProcessIdFileReader;
 import org.apache.hadoop.util.Shell;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import static org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch.CONTAINER_PRE_LAUNCH_STDERR;
 import static org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch.CONTAINER_PRE_LAUNCH_STDOUT;
@@ -851,7 +852,7 @@ public String getProcessId(ContainerId containerID) {
    * This class will signal a target container after a specified delay.
    * @see #signalContainer
    */
-  public static class DelayedProcessKiller extends Thread {
+  public static class DelayedProcessKiller extends SubjectInheritingThread {
     private final Container container;
     private final String user;
     private final String pid;
@@ -883,7 +884,7 @@ public DelayedProcessKiller(Container container, String user, String pid,
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         Thread.sleep(delay);
         containerExecutor.signalContainer(new ContainerSignalContext.Builder()
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeManager.java
index 6110e624f8d..d109de3c79d 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeManager.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeManager.java
@@ -40,6 +40,7 @@
 import org.apache.hadoop.util.Shell;
 import org.apache.hadoop.util.ShutdownHookManager;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.YarnUncaughtExceptionHandler;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ContainerId;
@@ -534,9 +535,9 @@ public String getName() {
   }
 
   protected void shutDown(final int exitCode) {
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           NodeManager.this.stop();
         } catch (Throwable t) {
@@ -559,9 +560,9 @@ protected void resyncWithRM() {
       // Some other thread is already created for resyncing, do nothing
     } else {
       // We have got the lock, create a new thread
-      new Thread() {
+      new SubjectInheritingThread() {
         @Override
-        public void run() {
+        public void work() {
           try {
             if (!rmWorkPreservingRestartEnabled) {
               LOG.info("Cleaning up running containers on resync");
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeResourceMonitorImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeResourceMonitorImpl.java
index 37fa33e14fc..bb5200c0745 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeResourceMonitorImpl.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeResourceMonitorImpl.java
@@ -20,6 +20,7 @@
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.service.AbstractService;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ResourceInformation;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.api.records.ResourceUtilization;
@@ -149,7 +150,7 @@ protected void serviceStop() throws Exception {
   /**
    * Thread that monitors the resource utilization of this node.
    */
-  private class MonitoringThread extends Thread {
+  private class MonitoringThread extends SubjectInheritingThread {
     /**
      * Initialize the node resource monitoring thread.
      */
@@ -162,7 +163,7 @@ public MonitoringThread() {
      * Periodically monitor the resource utilization of the node.
      */
     @Override
-    public void run() {
+    public void work() {
       while (true) {
         // Get node utilization and save it into the health status
         long pmem = resourceCalculatorPlugin.getPhysicalMemorySize() -
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeStatusUpdaterImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeStatusUpdaterImpl.java
index 5da709c49dc..e3b627ab80a 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeStatusUpdaterImpl.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/NodeStatusUpdaterImpl.java
@@ -44,6 +44,7 @@
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.VersionUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.protocolrecords.SignalContainerRequest;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ContainerId;
@@ -329,7 +330,7 @@ protected void rebootNodeStatusUpdaterAndRegisterWithRM() {
       try {
         statusUpdater.join();
         registerWithRM();
-        statusUpdater = new Thread(statusUpdaterRunnable, "Node Status Updater");
+        statusUpdater = new SubjectInheritingThread(statusUpdaterRunnable, "Node Status Updater");
         this.isStopped = false;
         statusUpdater.start();
         LOG.info("NodeStatusUpdater thread is reRegistered and restarted");
@@ -828,7 +829,7 @@ private static Map<ApplicationId, Credentials> parseCredentials(
   protected void startStatusUpdater() {
     statusUpdaterRunnable = new StatusUpdaterRunnable();
     statusUpdater =
-        new Thread(statusUpdaterRunnable, "Node Status Updater");
+        new SubjectInheritingThread(statusUpdaterRunnable, "Node Status Updater");
     statusUpdater.start();
   }
 
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/WindowsSecureContainerExecutor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/WindowsSecureContainerExecutor.java
index 78ba39ef693..7658cc9ee0b 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/WindowsSecureContainerExecutor.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/WindowsSecureContainerExecutor.java
@@ -52,6 +52,7 @@
 import org.apache.hadoop.util.NativeCodeLoader;
 import org.apache.hadoop.util.Shell;
 import org.apache.hadoop.util.Shell.CommandExecutor;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.Resource;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer;
@@ -497,10 +498,10 @@ public void validateResult() throws IOException {
     
     private Thread startStreamReader(final InputStream stream) 
         throws IOException {
-      Thread streamReaderThread = new Thread() {
+      Thread streamReaderThread = new SubjectInheritingThread() {
         
         @Override
-        public void run() {
+        public void work() {
           try (BufferedReader lines = new BufferedReader(
                    new InputStreamReader(stream, StandardCharsets.UTF_8))) {
             char[] buf = new char[512];
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/ContainerImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/ContainerImpl.java
index cdd9fc916e3..9da951e0558 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/ContainerImpl.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/container/ContainerImpl.java
@@ -48,6 +48,7 @@
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.security.Credentials;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ContainerExitStatus;
 import org.apache.hadoop.yarn.api.records.ContainerId;
 import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
@@ -1749,9 +1750,9 @@ private void doRelaunch(final ContainerImpl container,
         container.sendRelaunchEvent();
       } else {
         // wait for some time, then send launch event
-        new Thread() {
+        new SubjectInheritingThread() {
           @Override
-          public void run() {
+          public void work() {
             try {
               Thread.sleep(retryInterval);
               container.sendRelaunchEvent();
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupElasticMemoryController.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupElasticMemoryController.java
index e942983e011..db486dfb4d0 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupElasticMemoryController.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/linux/resources/CGroupElasticMemoryController.java
@@ -23,6 +23,7 @@
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.util.Shell;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationConstants;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.exceptions.YarnException;
@@ -55,7 +56,7 @@
  * events of all the containers together, and if we go over the limit picks
  * a container to kill. The algorithm that picks the container is a plugin.
  */
-public class CGroupElasticMemoryController extends Thread {
+public class CGroupElasticMemoryController extends SubjectInheritingThread {
   protected static final Logger LOG = LoggerFactory
       .getLogger(CGroupElasticMemoryController.class);
   private final Clock clock = new MonotonicClock();
@@ -238,7 +239,7 @@ public static boolean isAvailable() {
    * reasons.
    */
   @Override
-  public void run() {
+  public void work() {
     ExecutorService executor = null;
     try {
       // Disable OOM killer and set a limit.
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/localizer/ResourceLocalizationService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/localizer/ResourceLocalizationService.java
index a7f0722e66f..fcce814fd98 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/localizer/ResourceLocalizationService.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/localizer/ResourceLocalizationService.java
@@ -80,6 +80,7 @@
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.concurrent.HadoopExecutors;
 import org.apache.hadoop.util.concurrent.HadoopScheduledThreadPoolExecutor;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ContainerId;
 import org.apache.hadoop.yarn.api.records.LocalResource;
@@ -861,7 +862,7 @@ private static ExecutorService createLocalizerExecutor(Configuration conf) {
   }
 
 
-  class PublicLocalizer extends Thread {
+  class PublicLocalizer extends SubjectInheritingThread {
 
     final FileContext lfs;
     final Configuration conf;
@@ -975,7 +976,7 @@ private void createDir(Path dirPath, FsPermission perms)
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         // TODO shutdown, better error handling esp. DU
         while (!Thread.currentThread().isInterrupted()) {
@@ -1030,7 +1031,7 @@ public void run() {
    * access to user's credentials. One {@link LocalizerRunner} per localizerId.
    * 
    */
-  class LocalizerRunner extends Thread {
+  class LocalizerRunner extends SubjectInheritingThread {
 
     final LocalizerContext context;
     final String localizerId;
@@ -1254,7 +1255,7 @@ private Path getPathForLocalization(LocalResource rsrc,
 
     @Override
     @SuppressWarnings("unchecked") // dispatcher not typed
-    public void run() {
+    public void work() {
       Path nmPrivateCTokensPath = null;
       Throwable exception = null;
       try {
@@ -1405,7 +1406,7 @@ static String buildTokenFingerprint(Token<? extends TokenIdentifier> tk)
     return fingerprint.toString();
   }
 
-  static class CacheCleanup extends Thread {
+  static class CacheCleanup extends SubjectInheritingThread {
 
     private final Dispatcher dispatcher;
 
@@ -1416,7 +1417,7 @@ public CacheCleanup(Dispatcher dispatcher) {
 
     @Override
     @SuppressWarnings("unchecked") // dispatcher not typed
-    public void run() {
+    public void work() {
       dispatcher.getEventHandler().handle(
           new LocalizationEvent(LocalizationEventType.CACHE_CLEANUP));
     }
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/ContainersMonitorImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/ContainersMonitorImpl.java
index 0b4bd4a3fbd..f4869c6f62a 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/ContainersMonitorImpl.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/monitor/ContainersMonitorImpl.java
@@ -21,6 +21,7 @@
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.util.Preconditions;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.exceptions.YarnException;
 import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.CGroupElasticMemoryController;
 import org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.resources.ResourceHandlerModule;
@@ -489,13 +490,13 @@ boolean isProcessTreeOverLimit(ResourceCalculatorProcessTree pTree,
                                   curMemUsageOfAgedProcesses, limit);
   }
 
-  private class MonitoringThread extends Thread {
+  private class MonitoringThread extends SubjectInheritingThread {
     MonitoringThread() {
       super("Container Monitor");
     }
 
     @Override
-    public void run() {
+    public void work() {
 
       while (!stopped && !Thread.currentThread().isInterrupted()) {
         long start = Time.monotonicNow();
@@ -884,13 +885,13 @@ private String formatUsageString(long currentVmemUsage, long vmemLimit,
     }
   }
 
-  private class LogMonitorThread extends Thread {
+  private class LogMonitorThread extends SubjectInheritingThread {
     LogMonitorThread() {
       super("Container Log Monitor");
     }
 
     @Override
-    public void run() {
+    public void work() {
       while (!stopped && !Thread.currentThread().isInterrupted()) {
         for (Entry<ContainerId, ProcessTreeInfo> entry :
             trackingContainers.entrySet()) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java
index 86e1379223b..bb114e50c94 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestLinuxContainerExecutor.java
@@ -66,6 +66,7 @@
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.permission.FsPermission;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ContainerId;
@@ -462,8 +463,8 @@ public void testContainerKill() throws Exception {
     assumeTrue(shouldRun());
 
     final ContainerId sleepId = getNextContainerId();
-    Thread t = new Thread() {
-      public void run() {
+    SubjectInheritingThread t = new SubjectInheritingThread() {
+      public void work() {
         try {
           runAndBlock(sleepId, "sleep", "100");
         } catch (IOException|ConfigurationException e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestNodeManagerResync.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestNodeManagerResync.java
index ad7a1e7776c..8bf27b1bdee 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestNodeManagerResync.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestNodeManagerResync.java
@@ -48,6 +48,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.token.SecretManager;
 import org.apache.hadoop.util.Shell;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.protocolrecords.ContainerUpdateRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.ContainerUpdateResponse;
 import org.apache.hadoop.yarn.api.protocolrecords.GetContainerStatusesRequest;
@@ -744,9 +745,9 @@ protected void rebootNodeStatusUpdaterAndRegisterWithRM() {
       }
     }
 
-    class ContainerUpdateResourceThread extends Thread {
+    class ContainerUpdateResourceThread extends SubjectInheritingThread {
       @Override
-      public void run() {
+      public void work() {
         // Construct container resource increase request
         List<Token> increaseTokens = new ArrayList<Token>();
         // Add increase request.
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestNodeStatusUpdater.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestNodeStatusUpdater.java
index 89010bb3342..d663298d34c 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestNodeStatusUpdater.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/TestNodeStatusUpdater.java
@@ -69,6 +69,7 @@
 import org.apache.hadoop.service.ServiceOperations;
 import org.apache.hadoop.test.LambdaTestUtils;
 import org.apache.hadoop.util.concurrent.HadoopExecutors;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.protocolrecords.SignalContainerRequest;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
@@ -1173,7 +1174,7 @@ protected NodeStatusUpdater createNodeStatusUpdater(Context context,
     assertTrue(lastService instanceof NodeStatusUpdater,
         "last service is NOT the node status updater");
 
-    Thread starterThread = new Thread(() -> {
+    Thread starterThread = new SubjectInheritingThread(() -> {
       try {
         nm.start();
       } catch (Throwable e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/amrmproxy/TestableFederationInterceptor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/amrmproxy/TestableFederationInterceptor.java
index 5172e12b64e..984edf296bc 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/amrmproxy/TestableFederationInterceptor.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/amrmproxy/TestableFederationInterceptor.java
@@ -288,11 +288,11 @@ public TestableAMRequestHandlerThread(Configuration conf,
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         getUGIWithToken(getAttemptId())
             .doAs((PrivilegedExceptionAction<Object>) () -> {
-              TestableAMRequestHandlerThread.super.run();
+              TestableAMRequestHandlerThread.super.work();
               return null;
             });
       } catch (Exception e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/localizer/TestContainerLocalizer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/localizer/TestContainerLocalizer.java
index a30a13f0a13..4b223f2f76a 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/localizer/TestContainerLocalizer.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/localizer/TestContainerLocalizer.java
@@ -74,6 +74,7 @@
 import org.apache.hadoop.util.DiskChecker.DiskErrorException;
 import org.apache.hadoop.util.Shell;
 import org.apache.hadoop.util.Shell.ShellCommandExecutor;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.LocalResource;
 import org.apache.hadoop.yarn.api.records.LocalResourceType;
 import org.apache.hadoop.yarn.api.records.LocalResourceVisibility;
@@ -321,9 +322,9 @@ public void testMultipleLocalizers() throws Exception {
     FakeContainerLocalizer localizerB = testB.init();
 
     // run localization
-    Thread threadA = new Thread() {
+    SubjectInheritingThread threadA = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           localizerA.runLocalization(nmAddr);
         } catch (Exception e) {
@@ -331,9 +332,9 @@ public void run() {
         }
       }
     };
-    Thread threadB = new Thread() {
+    SubjectInheritingThread threadB = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           localizerB.runLocalization(nmAddr);
         } catch (Exception e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/util/TestCgroupsLCEResourcesHandler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/util/TestCgroupsLCEResourcesHandler.java
index 3b7d3011f91..0f69fe9bded 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/util/TestCgroupsLCEResourcesHandler.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/util/TestCgroupsLCEResourcesHandler.java
@@ -18,6 +18,7 @@
 package org.apache.hadoop.yarn.server.nodemanager.util;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ContainerId;
 import org.apache.hadoop.yarn.api.records.Resource;
 import org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor;
@@ -114,9 +115,9 @@ public void testDeleteCgroup() throws Exception {
     fos.close();
 
     final CountDownLatch latch = new CountDownLatch(1);
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         latch.countDown();
         try {
           Thread.sleep(200);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
index b2d5a9c9d39..f9b41047100 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
@@ -57,6 +57,7 @@
 import org.apache.hadoop.util.Time;
 import org.apache.hadoop.util.curator.ZKCuratorManager;
 import org.apache.hadoop.util.VersionInfo;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.YarnUncaughtExceptionHandler;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
@@ -1135,7 +1136,7 @@ private class SchedulerEventDispatcher extends
     SchedulerEventDispatcher(String name, int samplesPerMin) {
       super(scheduler, name);
       this.eventProcessorMonitor =
-          new Thread(new EventProcessorMonitor(getEventProcessorId(),
+          new SubjectInheritingThread(new EventProcessorMonitor(getEventProcessorId(),
               samplesPerMin));
       this.eventProcessorMonitor
           .setName("ResourceManager Event Processor Monitor");
@@ -1224,7 +1225,7 @@ private synchronized void handleTransitionToStandByInNewThread() {
       return;
     }
     Thread standByTransitionThread =
-        new Thread(activeServices.standByTransitionRunnable);
+        new SubjectInheritingThread(activeServices.standByTransitionRunnable);
     standByTransitionThread.setName("StandByTransitionThread");
     standByTransitionThread.start();
   }
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/amlauncher/ApplicationMasterLauncher.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/amlauncher/ApplicationMasterLauncher.java
index 9f4de2868a1..36436e89299 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/amlauncher/ApplicationMasterLauncher.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/amlauncher/ApplicationMasterLauncher.java
@@ -24,6 +24,7 @@
 import java.util.concurrent.TimeUnit;
 
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.conf.Configuration;
@@ -105,14 +106,14 @@ protected void serviceStop() throws Exception {
     launcherPool.shutdown();
   }
 
-  private class LauncherThread extends Thread {
+  private class LauncherThread extends SubjectInheritingThread {
     
     public LauncherThread() {
       super("ApplicationMaster Launcher");
     }
 
     @Override
-    public void run() {
+    public void work() {
       while (!this.isInterrupted()) {
         Runnable toLaunch;
         try {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/federation/FederationStateStoreService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/federation/FederationStateStoreService.java
index 6384736d62e..e19d5a24048 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/federation/FederationStateStoreService.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/federation/FederationStateStoreService.java
@@ -31,6 +31,7 @@
 import org.apache.hadoop.net.NetUtils;
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.concurrent.HadoopExecutors;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.exceptions.YarnException;
@@ -597,7 +598,7 @@ public int incrementCurrentKeyId() {
    */
   public void createCleanUpFinishApplicationThread(String stage) {
     String threadName = cleanUpThreadNamePrefix + "-" + stage;
-    Thread finishApplicationThread = new Thread(createCleanUpFinishApplicationThread());
+    Thread finishApplicationThread = new SubjectInheritingThread(createCleanUpFinishApplicationThread());
     finishApplicationThread.setName(threadName);
     finishApplicationThread.start();
     LOG.info("CleanUpFinishApplicationThread has been started {}.", threadName);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/metrics/TimelineServiceV1Publisher.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/metrics/TimelineServiceV1Publisher.java
index f1b80a946a7..9985e1997c8 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/metrics/TimelineServiceV1Publisher.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/metrics/TimelineServiceV1Publisher.java
@@ -29,6 +29,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.YarnApplicationState;
@@ -536,13 +537,13 @@ public void handle(TimelineV1PublishEvent event) {
     }
   }
 
-  private class PutEventThread extends Thread {
+  private class PutEventThread extends SubjectInheritingThread {
     PutEventThread() {
       super("PutEventThread");
     }
 
     @Override
-    public void run() {
+    public void work() {
       LOG.info("System metrics publisher will put events every " +
           String.valueOf(putEventInterval) + " milliseconds");
       while (!stopped && !Thread.currentThread().isInterrupted()) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/monitor/SchedulingMonitor.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/monitor/SchedulingMonitor.java
index b7d1220bf9f..89f7c1bfb24 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/monitor/SchedulingMonitor.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/monitor/SchedulingMonitor.java
@@ -27,6 +27,7 @@
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.service.AbstractService;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
 
 import org.apache.hadoop.classification.VisibleForTesting;
@@ -70,7 +71,7 @@ public void serviceStart() throws Exception {
     assert !stopped : "starting when already stopped";
     ses = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
       public Thread newThread(Runnable r) {
-        Thread t = new Thread(r);
+        Thread t = new SubjectInheritingThread(r);
         t.setName(getName());
         return t;
       }
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java
index f0990cf8fb0..41797ac57e0 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/ZKRMStateStore.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.security.token.delegation.DelegationKey;
 import org.apache.hadoop.util.ZKUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.util.curator.ZKCuratorManager;
 import org.apache.hadoop.util.curator.ZKCuratorManager.SafeTransaction;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
@@ -1468,13 +1469,13 @@ public void safeDeleteAndCheckNode(String path, List<ACL> fencingACL,
    * Helper class that periodically attempts creating a znode to ensure that
    * this RM continues to be the Active.
    */
-  private class VerifyActiveStatusThread extends Thread {
+  private class VerifyActiveStatusThread extends SubjectInheritingThread {
     VerifyActiveStatusThread() {
       super(VerifyActiveStatusThread.class.getName());
     }
 
     @Override
-    public void run() {
+    public void work() {
       try {
         while (!isFencedState()) {
           // Create and delete fencing node
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/attempt/RMAppAttemptImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/attempt/RMAppAttemptImpl.java
index 6010bd21a18..4fb423425b2 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/attempt/RMAppAttemptImpl.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/attempt/RMAppAttemptImpl.java
@@ -45,6 +45,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.token.Token;
 import org.apache.hadoop.util.StringInterner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptReport;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
@@ -1238,9 +1239,9 @@ public RMAppAttemptState transition(RMAppAttemptImpl appAttempt,
 
   private void retryFetchingAMContainer(final RMAppAttemptImpl appAttempt) {
     // start a new thread so that we are not blocking main dispatcher thread.
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           Thread.sleep(500);
         } catch (InterruptedException e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/AbstractYarnScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/AbstractYarnScheduler.java
index 3343c5f9311..885665ea72d 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/AbstractYarnScheduler.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/AbstractYarnScheduler.java
@@ -117,6 +117,7 @@
 
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.SettableFuture;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 
 @SuppressWarnings("unchecked")
@@ -1716,9 +1717,9 @@ public void update() {
    * Thread which calls {@link #update()} every
    * <code>updateInterval</code> milliseconds.
    */
-  private class UpdateThread extends Thread {
+  private class UpdateThread extends SubjectInheritingThread {
     @Override
-    public void run() {
+    public void work() {
       while (!Thread.currentThread().isInterrupted()) {
         try {
           synchronized (updateThreadMonitor) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/activities/ActivitiesManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/activities/ActivitiesManager.java
index 001c638801b..facb0a08b76 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/activities/ActivitiesManager.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/activities/ActivitiesManager.java
@@ -21,6 +21,7 @@
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.util.Lists;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.Resource;
 import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler;
 import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler;
@@ -295,7 +296,7 @@ private void dynamicallyUpdateAppActivitiesMaxQueueLengthIfNeeded() {
 
   @Override
   protected void serviceStart() throws Exception {
-    cleanUpThread = new Thread(new Runnable() {
+    cleanUpThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         while (!stopped && !Thread.currentThread().isInterrupted()) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java
index 02ffe83a6df..e36e032c297 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/CapacityScheduler.java
@@ -53,6 +53,7 @@
 import org.apache.hadoop.security.AccessControlException;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.Container;
@@ -638,7 +639,7 @@ public void setAsyncSchedulingConf(AsyncSchedulingConfiguration conf) {
     this.asyncSchedulingConf = conf;
   }
 
-  static class AsyncScheduleThread extends Thread {
+  static class AsyncScheduleThread extends SubjectInheritingThread {
 
     private final CapacityScheduler cs;
     private AtomicBoolean runSchedules = new AtomicBoolean(false);
@@ -650,7 +651,7 @@ public AsyncScheduleThread(CapacityScheduler cs) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       int debuggingLogCounter = 0;
       while (!Thread.currentThread().isInterrupted()) {
         try {
@@ -691,7 +692,7 @@ public void suspendSchedule() {
 
   }
 
-  static class ResourceCommitterService extends Thread {
+  static class ResourceCommitterService extends SubjectInheritingThread {
     private final CapacityScheduler cs;
     private BlockingQueue<ResourceCommitRequest<FiCaSchedulerApp, FiCaSchedulerNode>>
         backlogs = new LinkedBlockingQueue<>();
@@ -702,7 +703,7 @@ public ResourceCommitterService(CapacityScheduler cs) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       while (!Thread.currentThread().isInterrupted()) {
         try {
           ResourceCommitRequest<FiCaSchedulerApp, FiCaSchedulerNode> request =
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationFileLoaderService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationFileLoaderService.java
index 7fab417d893..1ad1bf029b2 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationFileLoaderService.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/AllocationFileLoaderService.java
@@ -38,6 +38,7 @@
 import org.apache.hadoop.security.authorize.AccessControlList;
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.XMLUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.QueueACL;
 import org.apache.hadoop.yarn.security.AccessType;
 import org.apache.hadoop.yarn.security.Permission;
@@ -118,7 +119,7 @@ public void serviceInit(Configuration conf) throws Exception {
     this.allocFile = getAllocationFile(conf);
     if (this.allocFile != null) {
       this.fs = allocFile.getFileSystem(conf);
-      reloadThread = new Thread(() -> {
+      reloadThread = new SubjectInheritingThread(() -> {
         while (running) {
           try {
             synchronized (this) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSPreemptionThread.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSPreemptionThread.java
index 221bb17ae5b..48c0c981a7e 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSPreemptionThread.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FSPreemptionThread.java
@@ -19,6 +19,7 @@
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ContainerStatus;
 import org.apache.hadoop.yarn.api.records.Resource;
@@ -39,7 +40,7 @@
 /**
  * Thread that handles FairScheduler preemption.
  */
-class FSPreemptionThread extends Thread {
+class FSPreemptionThread extends SubjectInheritingThread {
   private static final Logger LOG = LoggerFactory.
       getLogger(FSPreemptionThread.class);
   protected final FSContext context;
@@ -71,7 +72,7 @@ class FSPreemptionThread extends Thread {
   }
 
   @Override
-  public void run() {
+  public void work() {
     while (!Thread.interrupted()) {
       try {
         FSAppAttempt starvedApp = context.getStarvedApps().take();
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java
index a3e3ddfafe3..96e0b7944fc 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairScheduler.java
@@ -100,6 +100,7 @@
 
 import org.apache.hadoop.classification.VisibleForTesting;
 import org.apache.hadoop.util.Preconditions;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.SettableFuture;
 
 import org.slf4j.Logger;
@@ -315,10 +316,10 @@ public QueueManager getQueueManager() {
    * asynchronous to the node heartbeats.
    */
   @Deprecated
-  private class ContinuousSchedulingThread extends Thread {
+  private class ContinuousSchedulingThread extends SubjectInheritingThread {
 
     @Override
-    public void run() {
+    public void work() {
       while (!Thread.currentThread().isInterrupted()) {
         try {
           continuousSchedulingAttempt();
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/placement/MultiNodeSorter.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/placement/MultiNodeSorter.java
index 38af12719ef..4000e6de58b 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/placement/MultiNodeSorter.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/placement/MultiNodeSorter.java
@@ -34,6 +34,7 @@
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.ReflectionUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.NodeId;
 import org.apache.hadoop.yarn.exceptions.YarnException;
 import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
@@ -99,7 +100,7 @@ public void serviceStart() throws Exception {
     assert !stopped : "starting when already stopped";
     ses = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
       public Thread newThread(Runnable r) {
-        Thread t = new Thread(r);
+        Thread t = new SubjectInheritingThread(r);
         t.setName(getName());
         return t;
       }
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/DelegationTokenRenewer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/DelegationTokenRenewer.java
index 8e1214afc80..9b4cf0a6015 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/DelegationTokenRenewer.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/security/DelegationTokenRenewer.java
@@ -62,6 +62,7 @@
 import org.apache.hadoop.service.AbstractService;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.event.AbstractEvent;
@@ -200,7 +201,7 @@ protected void serviceStart() throws Exception {
     dtCancelThread.start();
     if (tokenKeepAliveEnabled) {
       delayedRemovalThread =
-          new Thread(new DelayedTokenRemovalRunnable(getConfig()),
+          new SubjectInheritingThread(new DelayedTokenRemovalRunnable(getConfig()),
               "DelayedTokenCanceller");
       delayedRemovalThread.start();
     }
@@ -347,7 +348,7 @@ public int hashCode() {
   }
   
   
-  private static class DelegationTokenCancelThread extends Thread {
+  private static class DelegationTokenCancelThread extends SubjectInheritingThread {
     private static class TokenWithConf {
       Token<?> token;
       Configuration conf;
@@ -377,7 +378,7 @@ public void cancelToken(Token<?> token,
       }
     }
 
-    public void run() {
+    public void work() {
       TokenWithConf tokenWithConf = null;
       while (true) {
         try {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/ACLsTestBase.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/ACLsTestBase.java
index 4391c4177bb..e701000b147 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/ACLsTestBase.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/ACLsTestBase.java
@@ -31,6 +31,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.authorize.AccessControlList;
 import org.apache.hadoop.service.Service.STATE;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationClientProtocol;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.ipc.YarnRPC;
@@ -89,8 +90,8 @@ this.queueACLsManager, getRMContext()
       protected void doSecureLogin() throws IOException {
       }
     };
-    new Thread() {
-      public void run() {
+    new SubjectInheritingThread() {
+      public void work() {
         resourceManager.start();
       };
     }.start();
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestApplicationACLs.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestApplicationACLs.java
index 7d7330ed9cf..bf124e51d9b 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestApplicationACLs.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestApplicationACLs.java
@@ -55,6 +55,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.authorize.AccessControlList;
 import org.apache.hadoop.service.Service.STATE;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.ApplicationClientProtocol;
 import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationsRequest;
 import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationReportRequest;
@@ -150,8 +151,8 @@ protected ClientRMService createClientRMService() {
             this.queueACLsManager, null);
       };
     };
-    new Thread() {
-      public void run() {
+    new SubjectInheritingThread() {
+      public void work() {
         UserGroupInformation.createUserForTesting(ENEMY, new String[] {});
         UserGroupInformation.createUserForTesting(FRIEND,
             new String[] { FRIENDLY_GROUP });
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java
index 677b8ab5349..b3db045279a 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java
@@ -71,6 +71,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Sets;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.MockApps;
 import org.apache.hadoop.yarn.api.ApplicationClientProtocol;
 import org.apache.hadoop.yarn.api.protocolrecords.ApplicationsRequestScope;
@@ -1532,9 +1533,9 @@ public void handle(Event rawEvent) {
     rmService.init(new Configuration());
 
     // submit an app and wait for it to block while in app submission
-    Thread t = new Thread() {
+    SubjectInheritingThread t = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           rmService.submitApplication(submitRequest1);
         } catch (YarnException | IOException e) {}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestLeaderElectorService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestLeaderElectorService.java
index a7e7253171f..cdd97efdbe7 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestLeaderElectorService.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestLeaderElectorService.java
@@ -26,6 +26,7 @@
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.ha.HAServiceProtocol.HAServiceState;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.conf.HAUtil;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
@@ -189,9 +190,9 @@ public void testExpireCurrentZKSession() throws Exception{
   public void testRMFailToTransitionToActive() throws Exception{
     conf.set(YarnConfiguration.RM_HA_ID, "rm1");
     final AtomicBoolean throwException = new AtomicBoolean(true);
-    Thread launchRM = new Thread() {
+    SubjectInheritingThread launchRM = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         rm1 = new MockRM(conf, true) {
           @Override
           synchronized void transitionToActive() throws Exception {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMHA.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMHA.java
index 0807c3aa321..884da531b4b 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMHA.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMHA.java
@@ -27,6 +27,7 @@
 
 import org.apache.hadoop.test.AbstractHadoopTestBase;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.server.resourcemanager.rmnode.RMNode;
 import org.apache.hadoop.yarn.server.resourcemanager.scheduler.event.NodeUpdateSchedulerEvent;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -518,7 +519,7 @@ void stopActiveServices() {
     rm.adminService.transitionToActive(requestInfo);
 
     // 3. Try Transition to standby
-    Thread t = new Thread(new Runnable() {
+    Thread t = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         try {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMHAForAsyncScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMHAForAsyncScheduler.java
index 4895987be4c..ef4bc6de7d8 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMHAForAsyncScheduler.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMHAForAsyncScheduler.java
@@ -236,14 +236,16 @@ private void checkAsyncSchedulerThreads(Thread currentThread){
     Thread resourceCommitterService = null;
     for (Thread thread : threads) {
       StackTraceElement[] stackTrace = thread.getStackTrace();
-      if(stackTrace.length>0){
-        String stackBottom = stackTrace[stackTrace.length-1].toString();
-        if(stackBottom.contains("AsyncScheduleThread.run")){
-          numAsyncScheduleThread++;
-          asyncScheduleThread = thread;
-        }else if(stackBottom.contains("ResourceCommitterService.run")){
-          numResourceCommitterService++;
-          resourceCommitterService = thread;
+      if (stackTrace.length > 0) {
+        for (StackTraceElement elem : stackTrace) {
+          String line = elem.toString();
+          if (line.contains("AsyncScheduleThread.work")) {
+            numAsyncScheduleThread++;
+            asyncScheduleThread = thread;
+          } else if (line.contains("ResourceCommitterService.work")) {
+            numResourceCommitterService++;
+            resourceCommitterService = thread;
+          }
         }
       }
     }
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java
index 15319a7e515..d83b862896f 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestFSRMStateStore.java
@@ -45,6 +45,7 @@
 import org.apache.hadoop.hdfs.MiniDFSCluster;
 import org.apache.hadoop.security.AccessControlException;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
@@ -414,7 +415,7 @@ public void testFSRMStateStoreClientRetry() throws Exception {
       final AtomicBoolean assertionFailedInThread = new AtomicBoolean(false);
       cluster.shutdownNameNodes();
 
-      Thread clientThread = new Thread(() -> {
+      Thread clientThread = new SubjectInheritingThread(() -> {
         try {
           store.storeApplicationStateInternal(
               ApplicationId.newInstance(100L, 1),
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStoreZKClientConnections.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStoreZKClientConnections.java
index d41c8235b5c..7c776175c38 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStoreZKClientConnections.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/recovery/TestZKRMStateStoreZKClientConnections.java
@@ -27,6 +27,7 @@
 import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
 import org.apache.hadoop.yarn.server.resourcemanager.recovery.RMStateStoreTestBase.TestDispatcher;
 import org.apache.hadoop.util.ZKUtil;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;
 import org.junit.jupiter.api.AfterEach;
@@ -112,9 +113,9 @@ public void testZKClientRetry() throws Exception {
     final AtomicBoolean assertionFailedInThread = new AtomicBoolean(false);
 
     testingServer.stop();
-    Thread clientThread = new Thread() {
+    SubjectInheritingThread clientThread = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           store.getData(path);
         } catch (Exception e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/resourcetracker/TestNMExpiry.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/resourcetracker/TestNMExpiry.java
index 7a0b49b878b..e09706002fd 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/resourcetracker/TestNMExpiry.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/resourcetracker/TestNMExpiry.java
@@ -26,6 +26,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.NodeId;
 import org.apache.hadoop.yarn.api.records.Resource;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
@@ -101,8 +102,8 @@ public void setUp() {
     resourceTrackerService.start();
   }
 
-  private class ThirdNodeHeartBeatThread extends Thread {
-    public void run() {
+  private class ThirdNodeHeartBeatThread extends SubjectInheritingThread {
+    public void work() {
       int lastResponseID = 0;
       while (!stopT) {
         try {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/TestQueueMetrics.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/TestQueueMetrics.java
index 64ac2562755..f0e74a68a38 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/TestQueueMetrics.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/TestQueueMetrics.java
@@ -24,6 +24,7 @@
 import org.apache.hadoop.metrics2.MetricsSystem;
 import org.apache.hadoop.metrics2.impl.MetricsSystemImpl;
 import org.apache.hadoop.test.MetricsAsserts;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
@@ -817,7 +818,7 @@ public void testQueueMetricsRaceCondition() throws InterruptedException {
      * simulate the concurrent calls for QueueMetrics#getQueueMetrics
      */
     // thread A will keep querying the same queue metrics for a specified number of iterations
-    Thread threadA = new Thread(() -> {
+    Thread threadA = new SubjectInheritingThread(() -> {
       try {
         for (int i = 0; i < numIterations; i++) {
           QueueMetrics qm = QueueMetrics.getQueueMetrics().get(queueName);
@@ -833,7 +834,7 @@ public void testQueueMetricsRaceCondition() throws InterruptedException {
       }
     });
     // thread B will keep adding new queue metrics for a specified number of iterations
-    Thread threadB = new Thread(() -> {
+    Thread threadB = new SubjectInheritingThread(() -> {
       try {
         for (int i = 0; i < numIterations; i++) {
           QueueMetrics.getQueueMetrics().put("q" + i, metrics);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacityScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacityScheduler.java
index c77bb26de82..ed76a5cad9e 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacityScheduler.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacityScheduler.java
@@ -111,6 +111,7 @@
 import org.apache.hadoop.security.token.TokenIdentifier;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.LocalConfigurationProvider;
 import org.apache.hadoop.yarn.api.ApplicationMasterProtocol;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest;
@@ -1064,7 +1065,7 @@ public ApplicationMasterProtocol run() {
     // grab the scheduler lock from another thread
     // and verify an allocate call in this thread doesn't block on it
     final CyclicBarrier barrier = new CyclicBarrier(2);
-    Thread otherThread = new Thread(new Runnable() {
+    Thread otherThread = new SubjectInheritingThread(new Runnable() {
       @Override
       public void run() {
         synchronized(cs) {
@@ -3088,7 +3089,7 @@ public void testRefreshQueueWithOpenPreemption() throws Exception {
 
       // The scheduler thread holds the queue's read-lock for 5 seconds
       // then the preemption's read-lock is used
-      Thread schedulerThread = new Thread(() -> {
+      Thread schedulerThread = new SubjectInheritingThread(() -> {
         queue.readLock.lock();
         try {
           Thread.sleep(5 * 1000);
@@ -3101,7 +3102,7 @@ public void testRefreshQueueWithOpenPreemption() throws Exception {
       }, "SCHEDULE");
 
       // The complete thread locks/unlocks the queue's write-lock after 1 seconds
-      Thread completeThread = new Thread(() -> {
+      Thread completeThread = new SubjectInheritingThread(() -> {
         try {
           Thread.sleep(1000);
         } catch (InterruptedException e) {
@@ -3115,7 +3116,7 @@ public void testRefreshQueueWithOpenPreemption() throws Exception {
       // The refresh thread holds the preemption's write-lock after 2 seconds
       // while it calls the getChildQueues(ByTryLock) that
       // locks(tryLocks) the queue's read-lock
-      Thread refreshThread = new Thread(() -> {
+      Thread refreshThread = new SubjectInheritingThread(() -> {
         try {
           Thread.sleep(2 * 1000);
         } catch (InterruptedException e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerAsyncScheduling.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerAsyncScheduling.java
index c2f6c823d16..62e2a5cbb11 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerAsyncScheduling.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerAsyncScheduling.java
@@ -31,6 +31,7 @@
 
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableList;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.util.ExitUtil;
 import org.apache.hadoop.yarn.api.records.Container;
@@ -800,7 +801,7 @@ public RMNodeLabelsManager createNodeLabelManager() {
     rm.close();
   }
 
-  public static class NMHeartbeatThread extends Thread {
+  public static class NMHeartbeatThread extends SubjectInheritingThread {
     private List<MockNM> mockNMS;
     private int interval;
     private volatile boolean shouldStop = false;
@@ -810,7 +811,7 @@ public NMHeartbeatThread(List<MockNM> mockNMs, int interval) {
       this.interval = interval;
     }
 
-    public void run() {
+    public void work() {
       while (true) {
         if (shouldStop) {
           break;
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerMultiNodes.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerMultiNodes.java
index 1a682e7e06f..fa5f1b288a8 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerMultiNodes.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerMultiNodes.java
@@ -39,6 +39,7 @@
 
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.thirdparty.com.google.common.collect.Iterators;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 
 import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
 import org.apache.hadoop.yarn.server.resourcemanager.RMContextImpl;
@@ -422,8 +423,8 @@ public void testAllocateOfReservedContainerFromAnotherNode()
             .build());
 
     final AtomicBoolean result = new AtomicBoolean(false);
-    Thread t = new Thread() {
-      public void run() {
+    Thread t = new SubjectInheritingThread() {
+      public void work() {
         try {
           MockAM am3 = MockRM.launchAndRegisterAM(app3, rm1, nm1);
           result.set(true);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerMultiNodesWithPreemption.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerMultiNodesWithPreemption.java
index 3edb87ebb90..3c105a93244 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerMultiNodesWithPreemption.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestCapacitySchedulerMultiNodesWithPreemption.java
@@ -24,6 +24,7 @@
 import static org.junit.jupiter.api.Assertions.fail;
 
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.Resource;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
@@ -171,8 +172,8 @@ public void testAllocateReservationFromOtherNode() throws Exception {
 
     // Launch AM in a thread and in parallel free the preempted node's
     // unallocated resources in main thread
-    Thread t1 = new Thread() {
-      public void run() {
+    Thread t1 = new SubjectInheritingThread() {
+      public void work() {
         try {
           MockAM am2 = MockRM.launchAM(app2, rm, nm1);
           result.set(true);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestLeafQueue.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestLeafQueue.java
index d977a7adab4..01326438e74 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestLeafQueue.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/capacity/TestLeafQueue.java
@@ -62,6 +62,7 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.authorize.AccessControlList;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
 import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
@@ -1129,7 +1130,7 @@ public void testUserLimitCache() throws Exception {
     // Set up allocation threads
     Thread[] threads = new Thread[numAllocationThreads];
     for (int i = 0; i < numAllocationThreads; i++) {
-      threads[i] = new Thread(new Runnable() {
+      threads[i] = new SubjectInheritingThread(new Runnable() {
         @Override
         public void run() {
           try {
@@ -4386,7 +4387,7 @@ public void testConcurrentAccess() throws Exception {
     final List<ConcurrentModificationException> conException =
         new ArrayList<ConcurrentModificationException>();
 
-    Thread submitAndRemove = new Thread(new Runnable() {
+    Thread submitAndRemove = new SubjectInheritingThread(new Runnable() {
 
       @Override
       public void run() {
@@ -4405,7 +4406,7 @@ public void run() {
       }
     }, "SubmitAndRemoveApplicationAttempt Thread");
 
-    Thread getAppsInQueue = new Thread(new Runnable() {
+    Thread getAppsInQueue = new SubjectInheritingThread(new Runnable() {
       List<ApplicationAttemptId> apps = new ArrayList<ApplicationAttemptId>();
 
       @Override
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairSchedulerWithMockPreemption.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairSchedulerWithMockPreemption.java
index 706cdc9034c..5958f799713 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairSchedulerWithMockPreemption.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/FairSchedulerWithMockPreemption.java
@@ -37,7 +37,7 @@ private MockPreemptionThread(FairScheduler scheduler) {
     }
 
     @Override
-    public void run() {
+    public void work() {
       while (!Thread.interrupted()) {
         try {
           FSAppAttempt app = context.getStarvedApps().take();
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestContinuousScheduling.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestContinuousScheduling.java
index f5254298dc4..009f84b0a49 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestContinuousScheduling.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/TestContinuousScheduling.java
@@ -22,6 +22,7 @@
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
 import org.apache.hadoop.yarn.api.records.ContainerId;
 import org.apache.hadoop.yarn.api.records.NodeId;
@@ -338,9 +339,9 @@ public void TestNodeAvailableResourceComparatorTransitivity() {
     }
 
     // To simulate unallocated resource changes
-    new Thread() {
+    new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         for (int j = 0; j < 100; j++) {
           for (FSSchedulerNode node : clusterNodeTracker.getAllNodes()) {
             int i = ThreadLocalRandom.current().nextInt(-30, 30);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/TestDominantResourceFairnessPolicy.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/TestDominantResourceFairnessPolicy.java
index 33fdba582ea..653fcaee608 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/TestDominantResourceFairnessPolicy.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/scheduler/fair/policies/TestDominantResourceFairnessPolicy.java
@@ -31,6 +31,7 @@
 import java.util.TreeSet;
 
 import org.apache.hadoop.thirdparty.com.google.common.base.Joiner;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.yarn.api.records.Resource;
 import org.apache.hadoop.yarn.api.records.ResourceInformation;
@@ -484,9 +485,9 @@ public void testModWhileSorting(){
    * Thread to simulate concurrent schedulable changes while sorting
    */
   private Thread modificationThread(final List<FakeSchedulable> schedulableList) {
-    Thread modThread  = new Thread() {
+    SubjectInheritingThread modThread  = new SubjectInheritingThread() {
       @Override
-      public void run() {
+      public void work() {
         try {
           // This sleep is needed to make sure the sort has started before the
           // modifications start and finish
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestDelegationTokenRenewer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestDelegationTokenRenewer.java
index 78c372c1b22..c64547cf652 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestDelegationTokenRenewer.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/security/TestDelegationTokenRenewer.java
@@ -74,6 +74,7 @@
 import org.apache.hadoop.security.token.delegation.DelegationKey;
 import org.apache.hadoop.test.GenericTestUtils;
 import org.apache.hadoop.util.StringUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.protocolrecords.SubmitApplicationRequest;
 import org.apache.hadoop.yarn.api.records.ApplicationAccessType;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
@@ -994,9 +995,9 @@ public Long answer(InvocationOnMock invocation)
     localDtr.init(conf);
     localDtr.start();
     // submit a job that blocks during renewal                                 
-    Thread submitThread = new Thread() {                                       
+    SubjectInheritingThread submitThread = new SubjectInheritingThread() {
       @Override                                                                
-      public void run() {
+      public void work() {
         localDtr.addApplicationAsync(mock(ApplicationId.class),
             creds1, false, "user", new Configuration());
       }                                                                        
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesSchedulerActivities.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesSchedulerActivities.java
index 6e574d79086..a027c87fb24 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesSchedulerActivities.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesSchedulerActivities.java
@@ -25,6 +25,7 @@
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.test.GenericTestUtils;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest;
 import org.apache.hadoop.yarn.conf.YarnConfiguration;
 import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler;
@@ -1735,7 +1736,7 @@ public void testSchedulerBulkActivities() throws Exception {
     }
   }
 
-  private class RESTClient extends Thread {
+  private class RESTClient extends SubjectInheritingThread {
 
     private int expectedCount;
     private boolean done = false;
@@ -1754,7 +1755,7 @@ JSONObject getOutput() {
     }
 
     @Override
-    public void run() {
+    public void work() {
       WebTarget r = targetWithJsonObject();
 
       Response response = r.path("ws").path("v1").path("cluster")
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/Router.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/Router.java
index 8f3c4d0fe57..fbd3b4efd07 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/Router.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/main/java/org/apache/hadoop/yarn/server/router/Router.java
@@ -47,6 +47,7 @@
 import org.apache.hadoop.util.ShutdownHookManager;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.hadoop.util.VersionInfo;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.util.GenericOptionsParser;
 import org.apache.hadoop.yarn.YarnUncaughtExceptionHandler;
 import org.apache.hadoop.yarn.api.records.ApplicationId;
@@ -202,7 +203,7 @@ protected void serviceStop() throws Exception {
   }
 
   protected void shutDown() {
-    new Thread(Router.this::stop).start();
+    new SubjectInheritingThread(Router.this::stop).start();
   }
 
   protected RouterClientRMService createClientRMProxyService() {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/clientrm/TestRouterClientRMService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/clientrm/TestRouterClientRMService.java
index 0c02fa1e8ca..9017188871f 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/clientrm/TestRouterClientRMService.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/clientrm/TestRouterClientRMService.java
@@ -29,6 +29,7 @@
 import java.util.Map;
 
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.api.protocolrecords.GetClusterMetricsResponse;
 import org.apache.hadoop.yarn.api.protocolrecords.GetClusterNodeLabelsResponse;
 import org.apache.hadoop.yarn.api.protocolrecords.GetClusterNodesResponse;
@@ -226,9 +227,9 @@ public void testClientPipelineConcurrent() throws InterruptedException {
      * ClientTestThread is a thread to simulate a client request to get a
      * ClientRequestInterceptor for the user.
      */
-    class ClientTestThread extends Thread {
+    class ClientTestThread extends SubjectInheritingThread {
       private ClientRequestInterceptor interceptor;
-      @Override public void run() {
+      @Override public void work() {
         try {
           interceptor = pipeline();
         } catch (IOException | InterruptedException e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/rmadmin/TestRouterRMAdminService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/rmadmin/TestRouterRMAdminService.java
index d4b02f4d951..1430e98ed3b 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/rmadmin/TestRouterRMAdminService.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/rmadmin/TestRouterRMAdminService.java
@@ -29,6 +29,7 @@
 import java.util.Map;
 
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.server.api.protocolrecords.AddToClusterNodeLabelsResponse;
 import org.apache.hadoop.yarn.server.api.protocolrecords.CheckForDecommissioningNodesResponse;
 import org.apache.hadoop.yarn.server.api.protocolrecords.RefreshAdminAclsResponse;
@@ -235,9 +236,9 @@ public void testRMAdminPipelineConcurrent() throws InterruptedException {
      * ClientTestThread is a thread to simulate a client request to get a
      * RMAdminRequestInterceptor for the user.
      */
-    class ClientTestThread extends Thread {
+    class ClientTestThread extends SubjectInheritingThread {
       private RMAdminRequestInterceptor interceptor;
-      @Override public void run() {
+      @Override public void work() {
         try {
           interceptor = pipeline();
         } catch (IOException | InterruptedException e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/TestRouterWebServices.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/TestRouterWebServices.java
index ceb75e38a24..7ad5220d675 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/TestRouterWebServices.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-router/src/test/java/org/apache/hadoop/yarn/server/router/webapp/TestRouterWebServices.java
@@ -31,6 +31,7 @@
 import javax.ws.rs.core.Response;
 
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.exceptions.YarnException;
 import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ActivitiesInfo;
 import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppActivitiesInfo;
@@ -297,9 +298,9 @@ public void testWebPipelineConcurrent() throws InterruptedException {
      * ClientTestThread is a thread to simulate a client request to get a
      * RESTRequestInterceptor for the user.
      */
-    class ClientTestThread extends Thread {
+    class ClientTestThread extends SubjectInheritingThread {
       private RESTRequestInterceptor interceptor;
-      @Override public void run() {
+      @Override public void work() {
         try {
           interceptor = pipeline();
         } catch (IOException | InterruptedException e) {
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-documentstore/src/main/java/org/apache/hadoop/yarn/server/timelineservice/documentstore/reader/cosmosdb/CosmosDBDocumentStoreReader.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-documentstore/src/main/java/org/apache/hadoop/yarn/server/timelineservice/documentstore/reader/cosmosdb/CosmosDBDocumentStoreReader.java
index d8a7a56ac19..4149439ecbb 100755
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-documentstore/src/main/java/org/apache/hadoop/yarn/server/timelineservice/documentstore/reader/cosmosdb/CosmosDBDocumentStoreReader.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-documentstore/src/main/java/org/apache/hadoop/yarn/server/timelineservice/documentstore/reader/cosmosdb/CosmosDBDocumentStoreReader.java
@@ -24,6 +24,7 @@
 import com.microsoft.azure.cosmosdb.rx.AsyncDocumentClient;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.util.Sets;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.server.timelineservice.reader.TimelineReaderContext;
 import org.apache.hadoop.yarn.server.timelineservice.documentstore.DocumentStoreUtils;
 import org.apache.hadoop.yarn.server.timelineservice.documentstore.collection.document.NoDocumentFoundException;
@@ -244,7 +245,7 @@ public synchronized void close() {
   }
 
   private void addShutdownHook() {
-    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+    Runtime.getRuntime().addShutdownHook(new SubjectInheritingThread(() -> {
       if (executorService != null) {
         executorService.shutdown();
       }
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-documentstore/src/main/java/org/apache/hadoop/yarn/server/timelineservice/documentstore/writer/cosmosdb/CosmosDBDocumentStoreWriter.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-documentstore/src/main/java/org/apache/hadoop/yarn/server/timelineservice/documentstore/writer/cosmosdb/CosmosDBDocumentStoreWriter.java
index 7cfb7f2fe41..0562ce64aa2 100755
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-documentstore/src/main/java/org/apache/hadoop/yarn/server/timelineservice/documentstore/writer/cosmosdb/CosmosDBDocumentStoreWriter.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-timelineservice-documentstore/src/main/java/org/apache/hadoop/yarn/server/timelineservice/documentstore/writer/cosmosdb/CosmosDBDocumentStoreWriter.java
@@ -35,6 +35,7 @@
 import com.microsoft.azure.cosmosdb.rx.AsyncDocumentClient;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.util.Time;
+import org.apache.hadoop.util.concurrent.SubjectInheritingThread;
 import org.apache.hadoop.yarn.server.timelineservice.metrics.PerNodeAggTimelineCollectorMetrics;
 import org.apache.hadoop.yarn.server.timelineservice.documentstore.DocumentStoreUtils;
 import org.apache.hadoop.yarn.server.timelineservice.documentstore.collection.CollectionType;
@@ -279,7 +280,7 @@ public synchronized void close() {
   }
 
   private void addShutdownHook() {
-    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+    Runtime.getRuntime().addShutdownHook(new SubjectInheritingThread(() -> {
       if (executorService != null) {
         executorService.shutdown();
       }


---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org